From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976134; cv=none; d=zoho.com; s=zohoarc; b=S4CE8vbY8P2U+o0k9coxbc+tfIr31iBs2cOCaEBgJfvCJEizxhmxVcKFlSUboi2MTnJ16Q8eRCtNHafGuuLmpq+JnB5J/4Ra7TnP2g153/g9thVfIHb28h4Vbb5bLf+DiJLVyp8lJHqAL1HNr0iJCRs/LdQgmolFtGl7HUYzXa4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976134; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=MDmVAYwx8q0wUeIZetNgk5hyqujgk4lKAT5RasQF4jg=; b=k24OWmoU+W5vxOCYKXTVmdM9oHnRUIEq8kqifGAXv7T1Kdq55tmqE5mcQ3J7hdtl8GcD5mSMzd34EvPtyqrCbSDe9vYzQA9saY+9VWzZodvyMASPj2ihUc1PQIWUKsx0qTbyrJXyVjzsKKXorC1PH7+Pqmyz2BIITDz8NQw1G/g= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976133837742.177639229362; Sat, 4 May 2019 06:22:13 -0700 (PDT) Received: from localhost ([127.0.0.1]:56594 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMucA-0003f2-GK for importer@patchew.org; Sat, 04 May 2019 09:22:06 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44368) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDQ-0007Xe-2H for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDN-0002wU-12 for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:16 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:43691) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002uX-NC for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:12 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDJ-000Jin-6v; Sat, 04 May 2019 09:40:09 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:31 +0100 Message-Id: <20190504083638.13380-2-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:02 -0400 Subject: [Qemu-devel] [PATCH v1 1/8] target/avr: Add instruction decoder X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" This utility module builds a decision tree to decode instructions, starting= from a human readable list of instruction bit patterns. Automatic tree generation will hopefully be more efficient and more maintai= nable than a hand-designed opcode parser. Tree generation happens at startup because this seemed simpler to implement= than adding a new build step. Signed-off-by: Sarah Harris --- target/avr/decode.c | 441 ++++++++++++++++++++++++++++++++++++++++++++ target/avr/decode.h | 68 +++++++ 2 files changed, 509 insertions(+) create mode 100644 target/avr/decode.c create mode 100644 target/avr/decode.h diff --git a/target/avr/decode.c b/target/avr/decode.c new file mode 100644 index 0000000000..a984806d96 --- /dev/null +++ b/target/avr/decode.c @@ -0,0 +1,441 @@ +/* + * AVR instruction decoder. + * + * Copyright (c) 2019 University of Kent + * Author: Sarah Harris + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +/* + * # Why is this here? + * This decoder takes a list of human readable descriptions of instructions + * and uses it to build a binary decision tree used to choose translation + * functions for opcodes. + * It's built like this because figuring out the structure of AVR instruct= ions + * was too hard and writing a Big Nested Switch by hand seemed too painful. + * This seems to be the simplest answer that doesn't use loads (>0.5MB) of= RAM. + * + * # How does it work? + * This is based J. R. Quinlan's ID3 algorithm, tweaked to add weights to = each + * instruction. + * Having a binary tree branch on opcode bits seems obvious, but the awkwa= rd + * part is deciding which order to test the bits. + * Getting the order right means that redundant bits can be ignored and fe= wer + * branches are needed; i.e. less memory and faster lookups. + * Here, the tests are ordered by an estimate of information gain based on + * Shannon Entropy. + * In short, we guess how much each bit tells us and pick the one that giv= es + * us most progress toward knowing which instruction we're seeing. + * The weights are currently only used to prioritise legal opcodes over + * illegal opcodes, which significantly reduces the tree size. + * + * # Why are you doing this at run time? + * It was easier than building and running a special purpose tool during + * QEMU's build process. + * The tree is only built once, during startup, and hopefully doesn't take= long + * enough to be noticeable. + */ + +#include +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/error-report.h" +#include "decode.h" + +/* #define DEBUG_DECODER */ + +/* Wide enough for the largest AVR instruction. */ +#define OPCODE_T uint16_t +#define OPCODE_SIZE 16 + +/* + * Probability estimate for each instruction. + * Larger values mean higher priority. + */ +#define WEIGHT_T uint64_t +#define WEIGHT_LEGAL (1 << 16) +#define WEIGHT_ILLEGAL 1 + +typedef union Tree Tree; + +typedef struct { + bool is_leaf; + /* Bit to test */ + uint bit; + const Tree *zero; + const Tree *one; +} Branch; + +typedef struct { + bool is_leaf; + TranslateFn decoder; + /* Instruction length in bits */ + uint32_t length; + const char *name; +} Leaf; + +union Tree { + bool is_leaf; + Branch branch; + Leaf leaf; +}; + +/* Additional (generated) instruction data */ +typedef struct { + const Instruction *instruction; + /* Instruction length in bits */ + uint32_t length; + WEIGHT_T weight; + /* + * Bit pattern matched in opcodes. + * For each 1 in mask, the same bit in the opcode must match that from= bits. + */ + OPCODE_T bits; + OPCODE_T mask; +} Pattern; + +/* Cached decoding tree */ +Tree *cache; + +/* Return calculated bit pattern and length for an instruction */ +static Pattern get_info(const Instruction *instruction) +{ + OPCODE_T bit =3D 1 << (OPCODE_SIZE - 1); + OPCODE_T bits =3D 0; + OPCODE_T mask =3D 0; + uint32_t length =3D 0; + const char *c =3D instruction->pattern; + while (*c !=3D '\0') { + switch (*c) { + case '0': + mask |=3D bit; + bit >>=3D 1; + length++; + break; + case '1': + bits |=3D bit; + mask |=3D bit; + bit >>=3D 1; + length++; + break; + case '*': + bit >>=3D 1; + length++; + break; + case '_': + /* NOP */ + break; + default: + assert(0); + } + c++; + } + const Pattern pattern =3D { + .instruction =3D instruction, + .length =3D length, + .weight =3D WEIGHT_LEGAL, + .bits =3D bits, + .mask =3D mask + }; + return pattern; +} + +/* Return true if an instruction matches a pattern of known/unknown bits */ +static bool matches(const Pattern *const pattern, const OPCODE_T bits, + const OPCODE_T mask) +{ + OPCODE_T overlap =3D pattern->mask & mask; + return (pattern->bits & overlap) =3D=3D (bits & overlap); +} + +/* Return number of instructions that match a pattern of known/unknown bit= s */ +static size_t count_legal(const Pattern *patterns, const size_t size, + const OPCODE_T bits, const OPCODE_T mask) +{ + size_t sum =3D 0; + size_t i; + for (i =3D 0; i < size; i++) { + if (matches(&patterns[i], bits, mask)) { + sum++; + } + } + return sum; +} + +/* Return the number of opcodes that could match a bit pattern */ +static uint64_t count_opcodes(const OPCODE_T _bits, OPCODE_T mask) +{ + uint64_t matches =3D 1; + int i; + assert(sizeof(matches) * 8 > OPCODE_SIZE); + for (i =3D 0; i < OPCODE_SIZE; i++) { + if (!(mask & 1)) { + matches <<=3D 1; + } + mask >>=3D 1; + } + return matches; +} + +/* + * Return a known/unknown bit pattern that only matches opcodes matched by= both + * of the given patterns. + */ +static void intersection(const OPCODE_T a_bits, const OPCODE_T a_mask, + const OPCODE_T b_bits, const OPCODE_T b_mask, + OPCODE_T *const out_bits, OPCODE_T *const out_mask) +{ + const OPCODE_T overlap =3D a_mask & b_mask; + /* The two patterns mustn't have conflicting requirements */ + assert((a_bits & overlap) =3D=3D (b_bits & overlap)); + *out_bits =3D (a_bits & a_mask) | (b_bits & b_mask); + *out_mask =3D a_mask | b_mask; +} + +/* + * Return one if any opcode allowed by a pattern of known/unknown bits is + * illegal. + */ +static size_t count_illegal(const Pattern *patterns, const size_t size, + const OPCODE_T bits, const OPCODE_T mask) +{ + size_t i; + const uint64_t no_opcodes =3D count_opcodes(bits, mask); + uint64_t no_legal =3D 0; + + /* Count opcodes that match instructions */ + for (i =3D 0; i < size; i++) { + const Pattern *pattern =3D &patterns[i]; + if (matches(pattern, bits, mask)) { + OPCODE_T both_bits, both_mask; + intersection( + bits, mask, + pattern->bits, pattern->mask, + &both_bits, &both_mask + ); + no_legal +=3D count_opcodes(both_bits, both_mask); + } + } + + assert(no_legal <=3D no_opcodes); + if (no_legal =3D=3D no_opcodes) { + return 0; + } else { + return 1; + } +} + +/* Return the first matching instruction for a pattern of known/unknown bi= ts */ +static const Pattern *find_match(const Pattern *patterns, const size_t siz= e, + const OPCODE_T bits, const OPCODE_T mask) +{ + size_t i; + for (i =3D 0; i < size; i++) { + if (matches(&patterns[i], bits, mask)) { + return &patterns[i]; + } + } + return NULL; +} + +/* Return sum of weights of instructions that match a bit pattern */ +static WEIGHT_T weigh_matches(const Pattern *patterns, const size_t size, + const OPCODE_T bits, const OPCODE_T mask) +{ + size_t i; + WEIGHT_T illegal =3D (WEIGHT_T)count_illegal(patterns, size, bits, mas= k) + * WEIGHT_ILLEGAL; + WEIGHT_T legal =3D 0; + for (i =3D 0; i < size; i++) { + if (matches(&patterns[i], bits, mask)) { + legal +=3D patterns[i].weight; + } + } + return legal + illegal; +} + +/* + * Return "effort" (estimated information needed) to decide tree outcome. + * bits and mask give the opcode bits already decided by the parent tree. + * parent_weight gives the sum of the weights of instructions that the + * parent tree matches. + */ +static float subtree_effort(const Pattern *patterns, const size_t size, + const OPCODE_T bits, const OPCODE_T mask, const WEIGHT_T parent_weight) +{ + const WEIGHT_T weight =3D weigh_matches(patterns, size, bits, mask); + float entropy_legal, entropy_illegal, probability; + size_t i; + + /* Sum information needed to decide legal instructions */ + entropy_legal =3D 0.0; + for (i =3D 0; i < size; i++) { + const Pattern *const pattern =3D &patterns[i]; + if (matches(pattern, bits, mask)) { + probability =3D (float)pattern->weight / (float)weight; + entropy_legal +=3D -probability * log2(probability); + } + } + + /* Sum information needed to decide illegal instructions */ + probability =3D (float)WEIGHT_ILLEGAL / (float)weight; + entropy_illegal =3D -probability * log2(probability) * + (float)count_illegal(patterns, size, bits, mask); + + return ((float)weight / (float)parent_weight) * + (entropy_legal + entropy_illegal); +} + +/* Return recursively built binary tree for decoding an opcode to instruct= ion */ +static Tree *build_tree(const Pattern *patterns, const size_t size, + OPCODE_T bits, OPCODE_T mask) +{ + /* Check if we've reached a leaf */ + size_t matching_illegal =3D count_illegal(patterns, size, bits, mask); + size_t matching_legal =3D count_legal(patterns, size, bits, mask); + size_t matching =3D matching_illegal + matching_legal; + assert(matching > 0); /* At last an illegal instruction should match */ + if (matching_legal =3D=3D 0) { + /* Illegal instruction */ + Leaf *leaf =3D g_new0(Leaf, 1); + leaf->is_leaf =3D true; + leaf->decoder =3D NULL; + leaf->length =3D 16; + leaf->name =3D "illegal"; + return (Tree *)leaf; + } + if (matching_legal =3D=3D 1 && matching_illegal =3D=3D 0) { + /* Legal instruction */ + const Pattern *pattern =3D find_match(patterns, size, bits, mask); + assert(pattern !=3D NULL); + const Instruction *const instruction =3D pattern->instruction; + Leaf *leaf =3D g_new0(Leaf, 1); + leaf->is_leaf =3D true; + leaf->decoder =3D instruction->decoder; + leaf->length =3D pattern->length; + leaf->name =3D instruction->name; + return (Tree *)leaf; + } + + /* Work out which bit to branch on */ + const WEIGHT_T tree_weight =3D weigh_matches(patterns, size, bits, mas= k); + float min_effort =3D 0.0; + ssize_t min_bit =3D -1; + size_t i; + for (i =3D 0; i < OPCODE_SIZE; i++) { + float effort; + const OPCODE_T bit =3D 1 << i; + if (mask & bit) { + /* This bit already branched on, skip */ + continue; + } + effort =3D subtree_effort(patterns, size, bits, mask | bit, tree_w= eight) + + subtree_effort(patterns, size, bits | bit, mask | bit, tree_we= ight); + if (min_bit < 0 || effort < min_effort) { + min_bit =3D i; + min_effort =3D effort; + } + } + + /* + * Setup branch on bit that gives most information gain. + * (AKA minimum information/effort needed to decide remaining branches. + */ + assert(min_bit >=3D 0); /* Probably multiple instructions match one op= code */ + const OPCODE_T bit =3D 1 << min_bit; + const Tree *zero =3D build_tree(patterns, size, bits, mask | bit); + const Tree *one =3D build_tree(patterns, size, bits | bit, mask | bit); + Branch *branch =3D g_new0(Branch, 1); + branch->is_leaf =3D false; + branch->bit =3D min_bit; + branch->zero =3D zero; + branch->one =3D one; + return (Tree *)branch; +} + +#ifdef DEBUG_DECODER +static size_t depth(const Tree *const tree); +static size_t depth(const Tree *const tree) +{ + if (tree->is_leaf) { + return 1; + } + size_t zero =3D depth(tree->branch.zero); + size_t one =3D depth(tree->branch.one); + if (zero > one) { + return zero + 1; + } + return one + 1; +} +static size_t count(const Tree *const tree); +static size_t count(const Tree *const tree) +{ + if (tree->is_leaf) { + return 1; + } + return 1 + count(tree->branch.zero) + count(tree->branch.one); +} +#endif + +void avr_decoder_init(const Instruction instructions[], const size_t size) +{ + assert(cache =3D=3D NULL); /* Shouldn't be initialised more than once = */ + Pattern *const patterns =3D g_new0(Pattern, size); + size_t i; + for (i =3D 0; i < size; i++) { + patterns[i] =3D get_info(&instructions[i]); + } + cache =3D build_tree(patterns, size, 0, 0); + g_free(patterns); +#ifdef DEBUG_DECODER + printf("AVR decoder init, depth=3D%lu, size=3D%lu\n", + depth(cache), count(cache)); +#endif +} + +TranslateFn avr_decode(const uint32_t opcode, uint32_t *const length_out) +{ + assert(cache !=3D NULL); /* Must be initialised */ + const Tree *node =3D cache; + while (1) { + assert(node !=3D NULL); + if (node->is_leaf) { + const Leaf *const leaf =3D &node->leaf; + if (leaf->decoder =3D=3D NULL) { + /* Illegal instruction */ + error_report("Illegal AVR instruction"); + exit(1); + } +#ifdef DEBUG_DECODER + printf("AVR decoder: %s\n", leaf->name); +#endif + *length_out =3D leaf->length; + return leaf->decoder; + } else { + const Branch *const branch =3D &node->branch; + const OPCODE_T mask =3D 1 << branch->bit; + if (opcode & mask) { + node =3D branch->one; + } else { + node =3D branch->zero; + } + } + } +} diff --git a/target/avr/decode.h b/target/avr/decode.h new file mode 100644 index 0000000000..415406544a --- /dev/null +++ b/target/avr/decode.h @@ -0,0 +1,68 @@ +/* + * AVR instruction decoder. + * + * Copyright (c) 2019 University of Kent + * Author: Sarah Harris + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +#ifndef AVR_DECODER_H +#define AVR_DECODER_H + +/* Pointer to functions used to do final decoding step from opcode to TCG.= */ +typedef struct DisasContext DisasContext; +typedef int (*TranslateFn)(DisasContext *ctx, uint32_t opcode); + +/* + * Human readable instruction descriptions used to generate decoder. + * Doing this at runtime avoids a complicated new build step. + */ +typedef struct { + /* Instruction mnemonic for debugging */ + const char *name; + /* + * Bit pattern describing the instruction's opcode. + * Each character represents a bit: + * - '1' means bit must be set + * - '0' means bit must be cleared + * - '*' means don't care + * - '_' is ignored (i.e. whitespace), please use to aid readability + */ + const char *pattern; + /* Function used to translate this instruction to TCG */ + TranslateFn decoder; +} Instruction; + +/* + * Converts a list of instruction descriptions to a decoding tree + * and caches it. + * Must only be called once. + * `size` is the number of instructions in the given list. + */ +void avr_decoder_init(const Instruction instructions[], const size_t size); + +/* + * Returns the translation function and length of an instruction, given + * the opcode. + * avr_decoder_init() must be called first to build the decoding tree. + */ +TranslateFn avr_decode(const uint32_t opcode, uint32_t *const length_out); + +#endif /* AVR_DECODER_H */ --=20 2.21.0 From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976170; cv=none; d=zoho.com; s=zohoarc; b=R0hXGr1AWxD4TQIPGGBbP9J20Re5eC27mvvBLcDoOjy/UDH2beAmI+ukLbiJvwXkQUZXSHhKNnYqHaFk54ErwJPNFsV6yaQ6m7gSmbagi0k63DCK5Jndu999f+pQ61Klzk7SWbk9f33AmcOulSeKCB1G8PiCxH3JD5u/w3vhi8g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976170; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=vaeKiflS28c5uDsRful5ywEKlRyNRX/8Z+Dr/zDTAgo=; b=CqbYY3XqXCBvCClzUR/q8A1yuoiwL9wnPK8TR86WpDROAZN2ohkZVpSJwzLCdHFs3gZ2prfYqqmzEXT5AmA9b2OU0vBnVJP3kN8cIqExFWCIeR4auCHhTKoVidVhV2fO6HMaokY6AmWKD7hh38nEf2oGEoMywNH58k8+Vs6/1/s= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976170609376.5829488085983; Sat, 4 May 2019 06:22:50 -0700 (PDT) Received: from localhost ([127.0.0.1]:56596 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMucp-0004Bh-Gi for importer@patchew.org; Sat, 04 May 2019 09:22:47 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44329) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDO-0007WP-7G for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDM-0002w5-SZ for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:14 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:43191) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002uV-M0 for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:12 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDJ-000Jin-A6; Sat, 04 May 2019 09:40:09 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:32 +0100 Message-Id: <20190504083638.13380-3-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:01 -0400 Subject: [Qemu-devel] [PATCH v1 2/8] target/avr: Add mechanism to check for active debugger connection X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" AVR CPUs have a BREAK instruction which behaves differently depending on wh= ether debugging is enabled. Since the hardware fuses that normally control this are difficult to emulat= e, and the BREAK instruction is useful for testing, the BREAK instruction i= s instead enabled/disabled depending on whether a GDB session is attached. Signed-off-by: Sarah Harris --- gdbstub.c | 5 +++++ include/exec/gdbstub.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index d54abd17cc..a254a364e6 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1793,6 +1793,11 @@ static int gdb_handle_packet(GDBState *s, const char= *line_buf) return RS_IDLE; } =20 +bool gdb_is_active(void) +{ + return gdbserver_state !=3D NULL; +} + void gdb_set_stop_cpu(CPUState *cpu) { GDBProcess *p =3D gdb_get_cpu_process(gdbserver_state, cpu); diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 08363969c1..d059bf5339 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -45,6 +45,10 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const ch= ar *fmt, ...); */ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list = va); int use_gdb_syscalls(void); +/** + * gdb_is_active: return true if debugging in progress + */ +bool gdb_is_active(void); void gdb_set_stop_cpu(CPUState *cpu); void gdb_exit(CPUArchState *, int); #ifdef CONFIG_USER_ONLY --=20 2.21.0 From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976466; cv=none; d=zoho.com; s=zohoarc; b=kPtMKodVBrHrQpYajJIOoYp+8S637cmJbB7lRHPA8JMCn7WT+15D3nqyCOl2xz65RTE1N4kV52PZZKIsOmGHNdIqNv35zRcWUMkor/UubYr5SE7YSOxNhc6vPehj8o2gPr5B6mJc4RN78ohO40xtZC6e53iMyoOB4YYbRq8Ue28= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976466; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=np96pMfQ9ek/sz8In3+VanhxGir+oPoBhrbkoz6n7Qg=; b=adpu4bFcz01rioBtmfWAptQgm/hOYaaMeCIBkTPUE+O0GwVcAySMLDjzZ8puB6yeygmJ3wYu/YY+UQeKAUDNLTgPfO50GDTwHd04OuNTGaJvFppxS6eTPGlCNrOje/CDsD/yNVmYemvwRuzgAX1t0wN2q+eRNLhG4iatf49/We4= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976466510971.7014893355075; Sat, 4 May 2019 06:27:46 -0700 (PDT) Received: from localhost ([127.0.0.1]:56672 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMuhb-0000uN-3o for importer@patchew.org; Sat, 04 May 2019 09:27:43 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44391) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDR-0007Yl-Ht for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDN-0002wc-3I for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:17 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:51376) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002ub-NJ for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:12 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDJ-000Jin-CZ; Sat, 04 May 2019 09:40:09 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:33 +0100 Message-Id: <20190504083638.13380-4-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:02 -0400 Subject: [Qemu-devel] [PATCH v1 3/8] target/avr: Add outward facing interfaces and core CPU logic X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" This includes: - CPU data structures - object model classes and functions - migration functions - GDB hooks Signed-off-by: Sarah Harris --- target/avr/cpu-qom.h | 83 +++++++ target/avr/cpu.c | 570 +++++++++++++++++++++++++++++++++++++++++++ target/avr/cpu.h | 238 ++++++++++++++++++ target/avr/gdbstub.c | 85 +++++++ target/avr/machine.c | 122 +++++++++ 5 files changed, 1098 insertions(+) create mode 100644 target/avr/cpu-qom.h create mode 100644 target/avr/cpu.c create mode 100644 target/avr/cpu.h create mode 100644 target/avr/gdbstub.c create mode 100644 target/avr/machine.c diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h new file mode 100644 index 0000000000..8085567b87 --- /dev/null +++ b/target/avr/cpu-qom.h @@ -0,0 +1,83 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#ifndef QEMU_AVR_CPU_QOM_H +#define QEMU_AVR_CPU_QOM_H + +#include "qom/cpu.h" + +#define TYPE_AVR_CPU "avr" + +#define AVR_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(AVRCPUClass, (klass), TYPE_AVR_CPU) +#define AVR_CPU(obj) \ + OBJECT_CHECK(AVRCPU, (obj), TYPE_AVR_CPU) +#define AVR_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AVRCPUClass, (obj), TYPE_AVR_CPU) + +/** + * AVRCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * @vr: Version Register value. + * + * A AVR CPU model. + */ +typedef struct AVRCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + void (*parent_reset)(CPUState *cpu); +} AVRCPUClass; + +/** + * AVRCPU: + * @env: #CPUAVRState + * + * A AVR CPU. + */ +typedef struct AVRCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUAVRState env; +} AVRCPU; + +static inline AVRCPU *avr_env_get_cpu(CPUAVRState *env) +{ + return container_of(env, AVRCPU, env); +} + +#define ENV_GET_CPU(e) CPU(avr_env_get_cpu(e)) +#define ENV_OFFSET offsetof(AVRCPU, env) + +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vms_avr_cpu; +#endif + +void avr_cpu_do_interrupt(CPUState *cpu); +bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req); +void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags); +hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int avr_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int avr_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +#endif diff --git a/target/avr/cpu.c b/target/avr/cpu.c new file mode 100644 index 0000000000..19ce1d7386 --- /dev/null +++ b/target/avr/cpu.c @@ -0,0 +1,570 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "qemu/qemu-print.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" +#include "migration/vmstate.h" + +static void avr_cpu_set_pc(CPUState *cs, vaddr value) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + + cpu->env.pc_w =3D value / 2; /* internally PC points to words */ +} + +static bool avr_cpu_has_work(CPUState *cs) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RE= SET)) + && cpu_interrupts_enabled(env); +} + +static void avr_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + env->pc_w =3D tb->pc / 2; /* internally PC points to words */ +} + +static void avr_cpu_reset(CPUState *s) +{ + AVRCPU *cpu =3D AVR_CPU(s); + AVRCPUClass *mcc =3D AVR_CPU_GET_CLASS(cpu); + CPUAVRState *env =3D &cpu->env; + + mcc->parent_reset(s); + + env->pc_w =3D 0; + env->sregI =3D 1; + env->sregC =3D 0; + env->sregZ =3D 0; + env->sregN =3D 0; + env->sregV =3D 0; + env->sregS =3D 0; + env->sregH =3D 0; + env->sregT =3D 0; + + env->rampD =3D 0; + env->rampX =3D 0; + env->rampY =3D 0; + env->rampZ =3D 0; + env->eind =3D 0; + env->sp =3D 0; + + memset(env->r, 0, sizeof(env->r)); + + tlb_flush(s); +} + +static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach =3D bfd_arch_avr; + info->print_insn =3D NULL; +} + +static void avr_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs =3D CPU(dev); + AVRCPUClass *mcc =3D AVR_CPU_GET_CLASS(dev); + Error *local_err =3D NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err !=3D NULL) { + error_propagate(errp, local_err); + return; + } + qemu_init_vcpu(cs); + cpu_reset(cs); + + mcc->parent_realize(dev, errp); +} + +static void avr_cpu_set_int(void *opaque, int irq, int level) +{ + AVRCPU *cpu =3D opaque; + CPUAVRState *env =3D &cpu->env; + CPUState *cs =3D CPU(cpu); + + uint64_t mask =3D (1ull << irq); + if (level) { + env->intsrc |=3D mask; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + env->intsrc &=3D ~mask; + if (env->intsrc =3D=3D 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +static void avr_cpu_initfn(Object *obj) +{ + CPUState *cs =3D CPU(obj); + AVRCPU *cpu =3D AVR_CPU(obj); + + cs->env_ptr =3D &cpu->env; + +#ifndef CONFIG_USER_ONLY + /* Set the number of interrupts supported by the CPU. */ + qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, 57); +#endif +} + +static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *name; + + if (!cpu_model) { + return NULL; + } + + oc =3D object_class_by_name(cpu_model); + if (oc !=3D NULL && object_class_dynamic_cast(oc, TYPE_AVR_CPU) !=3D N= ULL && + !object_class_is_abstract(oc)) { + return oc; + } + + name =3D g_strdup_printf(AVR_CPU_TYPE_NAME("%s"), cpu_model); + oc =3D object_class_by_name(name); + g_free(name); + if (oc !=3D NULL && object_class_dynamic_cast(oc, TYPE_AVR_CPU) !=3D N= ULL && + !object_class_is_abstract(oc)) { + return oc; + } + + return NULL; +} + +static void avr_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(oc); + CPUClass *cc =3D CPU_CLASS(oc); + AVRCPUClass *mcc =3D AVR_CPU_CLASS(oc); + + mcc->parent_realize =3D dc->realize; + dc->realize =3D avr_cpu_realizefn; + + mcc->parent_reset =3D cc->reset; + cc->reset =3D avr_cpu_reset; + + cc->class_by_name =3D avr_cpu_class_by_name; + + cc->has_work =3D avr_cpu_has_work; + cc->do_interrupt =3D avr_cpu_do_interrupt; + cc->cpu_exec_interrupt =3D avr_cpu_exec_interrupt; + cc->dump_state =3D avr_cpu_dump_state; + cc->set_pc =3D avr_cpu_set_pc; +#if !defined(CONFIG_USER_ONLY) + cc->memory_rw_debug =3D avr_cpu_memory_rw_debug; +#endif +#ifdef CONFIG_USER_ONLY + cc->handle_mmu_fault =3D avr_cpu_handle_mmu_fault; +#else + cc->get_phys_page_debug =3D avr_cpu_get_phys_page_debug; + cc->vmsd =3D &vms_avr_cpu; +#endif + cc->disas_set_info =3D avr_cpu_disas_set_info; + cc->tcg_initialize =3D avr_cpu_tcg_init; + cc->synchronize_from_tb =3D avr_cpu_synchronize_from_tb; + cc->gdb_read_register =3D avr_cpu_gdb_read_register; + cc->gdb_write_register =3D avr_cpu_gdb_write_register; + cc->gdb_num_core_regs =3D 35; +} + +static void avr_avr1_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); +} + +static void avr_avr2_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); +} + +static void avr_avr25_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); +} + +static void avr_avr3_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); +} + +static void avr_avr31_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); +} + +static void avr_avr35_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); +} + +static void avr_avr4_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_avr5_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_avr51_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_avr6_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_3_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_EIJMP_EICALL); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_xmega2_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega4_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega5_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPD); + avr_set_feature(env, AVR_FEATURE_RAMPX); + avr_set_feature(env, AVR_FEATURE_RAMPY); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega6_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_3_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_EIJMP_EICALL); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega7_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_3_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPD); + avr_set_feature(env, AVR_FEATURE_RAMPX); + avr_set_feature(env, AVR_FEATURE_RAMPY); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_EIJMP_EICALL); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +typedef struct AVRCPUInfo { + const char *name; + void (*initfn)(Object *obj); +} AVRCPUInfo; + +static gint avr_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *class_a =3D (ObjectClass *)a; + ObjectClass *class_b =3D (ObjectClass *)b; + const char *name_a; + const char *name_b; + + name_a =3D object_class_get_name(class_a); + name_b =3D object_class_get_name(class_b); + + return strcmp(name_a, name_b); +} + +static void avr_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc =3D data; + const char *typename =3D object_class_get_name(oc); + size_t len =3D strlen(typename); + size_t suffix_len =3D strlen(AVR_CPU_TYPE_SUFFIX); + + if (len > suffix_len) { + qemu_printf(" %.*s\n", (int)(len - suffix_len), typename); + } else { + qemu_printf(" %s\n", typename); + } +} + +void avr_cpu_list(void) +{ + GSList *list; + list =3D object_class_get_list(TYPE_AVR_CPU, false); + list =3D g_slist_sort(list, avr_cpu_list_compare); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, avr_cpu_list_entry, NULL); + g_slist_free(list); +} + +#define DEFINE_AVR_CPU_TYPE(model, initfn) \ + { \ + .parent =3D TYPE_AVR_CPU, \ + .instance_init =3D initfn, \ + .name =3D AVR_CPU_TYPE_NAME(model), \ + } + +static const TypeInfo avr_cpu_type_info[] =3D { + { + .name =3D TYPE_AVR_CPU, + .parent =3D TYPE_CPU, + .instance_size =3D sizeof(AVRCPU), + .instance_init =3D avr_cpu_initfn, + .class_size =3D sizeof(AVRCPUClass), + .class_init =3D avr_cpu_class_init, + .abstract =3D false, + }, + DEFINE_AVR_CPU_TYPE("avr1", avr_avr1_initfn), + DEFINE_AVR_CPU_TYPE("avr2", avr_avr2_initfn), + DEFINE_AVR_CPU_TYPE("avr25", avr_avr25_initfn), + DEFINE_AVR_CPU_TYPE("avr3", avr_avr3_initfn), + DEFINE_AVR_CPU_TYPE("avr31", avr_avr31_initfn), + DEFINE_AVR_CPU_TYPE("avr35", avr_avr35_initfn), + DEFINE_AVR_CPU_TYPE("avr4", avr_avr4_initfn), + DEFINE_AVR_CPU_TYPE("avr5", avr_avr5_initfn), + DEFINE_AVR_CPU_TYPE("avr51", avr_avr51_initfn), + DEFINE_AVR_CPU_TYPE("avr6", avr_avr6_initfn), + DEFINE_AVR_CPU_TYPE("xmega2", avr_xmega2_initfn), + DEFINE_AVR_CPU_TYPE("xmega4", avr_xmega4_initfn), + DEFINE_AVR_CPU_TYPE("xmega5", avr_xmega5_initfn), + DEFINE_AVR_CPU_TYPE("xmega6", avr_xmega6_initfn), + DEFINE_AVR_CPU_TYPE("xmega7", avr_xmega7_initfn), +}; + +DEFINE_TYPES(avr_cpu_type_info) diff --git a/target/avr/cpu.h b/target/avr/cpu.h new file mode 100644 index 0000000000..ee9e827070 --- /dev/null +++ b/target/avr/cpu.h @@ -0,0 +1,238 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#if !defined(CPU_AVR_H) +#define CPU_AVR_H + +#include "qemu-common.h" + +#define TARGET_LONG_BITS 32 + +#define CPUArchState struct CPUAVRState + +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +/* + * TARGET_PAGE_BITS cannot be more than 8 bits because + * 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they + * should be implemented as a device and not memory + * 2. SRAM starts at the address 0x0100 + */ +#define TARGET_PAGE_BITS 8 +#define TARGET_PHYS_ADDR_SPACE_BITS 24 +#define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define NB_MMU_MODES 2 + +/* + * AVR has two memory spaces, data & code. + * e.g. both have 0 address + * ST/LD instructions access data space + * LPM/SPM and instruction fetching access code memory space + */ +#define MMU_CODE_IDX 0 +#define MMU_DATA_IDX 1 + +#define EXCP_RESET 1 +#define EXCP_INT(n) (EXCP_RESET + (n) + 1) + +/* Number of CPU registers */ +#define NO_CPU_REGISTERS 32 +/* Number of IO registers accessible by ld/st/in/out */ +#define NO_IO_REGISTERS 64 + +/* + * Offsets of AVR memory regions in host memory space. + * + * This is needed because the AVR has separate code and data address + * spaces that both have start from zero but have to go somewhere in + * host memory. + * + * It's also useful to know where some things are, like the IO registers. + */ +/* Flash program memory */ +#define OFFSET_CODE 0x00000000 +/* CPU registers, IO registers, and SRAM */ +#define OFFSET_DATA 0x00800000 +/* CPU registers specifically, these are mapped at the start of data */ +#define OFFSET_CPU_REGISTERS OFFSET_DATA +/* + * IO registers, including status register, stack pointer, and memory + * mapped peripherals, mapped just after CPU registers + */ +#define OFFSET_IO_REGISTERS (OFFSET_DATA + NO_CPU_REGISTERS) + +enum avr_features { + AVR_FEATURE_SRAM, + + AVR_FEATURE_1_BYTE_PC, + AVR_FEATURE_2_BYTE_PC, + AVR_FEATURE_3_BYTE_PC, + + AVR_FEATURE_1_BYTE_SP, + AVR_FEATURE_2_BYTE_SP, + + AVR_FEATURE_BREAK, + AVR_FEATURE_DES, + AVR_FEATURE_RMW, /* Read Modify Write - XCH LAC LAS LAT */ + + AVR_FEATURE_EIJMP_EICALL, + AVR_FEATURE_IJMP_ICALL, + AVR_FEATURE_JMP_CALL, + + AVR_FEATURE_ADIW_SBIW, + + AVR_FEATURE_SPM, + AVR_FEATURE_SPMX, + + AVR_FEATURE_ELPMX, + AVR_FEATURE_ELPM, + AVR_FEATURE_LPMX, + AVR_FEATURE_LPM, + + AVR_FEATURE_MOVW, + AVR_FEATURE_MUL, + AVR_FEATURE_RAMPD, + AVR_FEATURE_RAMPX, + AVR_FEATURE_RAMPY, + AVR_FEATURE_RAMPZ, +}; + +typedef struct CPUAVRState CPUAVRState; + +struct CPUAVRState { + uint32_t pc_w; /* 0x003fffff up to 22 bits */ + + uint32_t sregC; /* 0x00000001 1 bits */ + uint32_t sregZ; /* 0x0000ffff 16 bits, negative logic; */ + /* 0=3Dflag set, >0=3Dflag cleared */ + uint32_t sregN; /* 0x00000001 1 bits */ + uint32_t sregV; /* 0x00000001 1 bits */ + uint32_t sregS; /* 0x00000001 1 bits */ + uint32_t sregH; /* 0x00000001 1 bits */ + uint32_t sregT; /* 0x00000001 1 bits */ + uint32_t sregI; /* 0x00000001 1 bits */ + + uint32_t rampD; /* 0x00ff0000 8 bits */ + uint32_t rampX; /* 0x00ff0000 8 bits */ + uint32_t rampY; /* 0x00ff0000 8 bits */ + uint32_t rampZ; /* 0x00ff0000 8 bits */ + uint32_t eind; /* 0x00ff0000 8 bits */ + + uint32_t r[NO_CPU_REGISTERS]; /* 8 bits each */ + uint32_t sp; /* 16 bits */ + + uint64_t intsrc; /* interrupt sources */ + bool fullacc; /* CPU/MEM if true MEM only otherwise */ + + uint32_t features; + + /* Those resources are used only in QEMU core */ + CPU_COMMON +}; + +static inline int avr_feature(CPUAVRState *env, int feature) +{ + return (env->features & (1U << feature)) !=3D 0; +} + +static inline void avr_set_feature(CPUAVRState *env, int feature) +{ + env->features |=3D (1U << feature); +} + +#define cpu_list avr_cpu_list +#define cpu_signal_handler cpu_avr_signal_handler + +#include "exec/cpu-all.h" +#include "cpu-qom.h" + +#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU +#define AVR_CPU_TYPE_NAME(model) model AVR_CPU_TYPE_SUFFIX +#define CPU_RESOLVING_TYPE TYPE_AVR_CPU + +static inline int cpu_mmu_index(CPUAVRState *env, bool ifetch) +{ + return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; +} + +void avr_cpu_tcg_init(void); + +void avr_cpu_list(void); +int cpu_avr_exec(CPUState *cpu); +int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc); +int avr_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, + int rw, int mmu_idx); +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf, + int len, bool is_write); + +enum { + TB_FLAGS_FULL_ACCESS =3D 1, +}; + +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *pflags) +{ + uint32_t flags =3D 0; + + *pc =3D env->pc_w * 2; + *cs_base =3D 0; + + if (env->fullacc) { + flags |=3D TB_FLAGS_FULL_ACCESS; + } + + *pflags =3D flags; +} + +static inline int cpu_interrupts_enabled(CPUAVRState *env) +{ + return env->sregI !=3D 0; +} + +static inline uint8_t cpu_get_sreg(CPUAVRState *env) +{ + uint8_t sreg; + sreg =3D (env->sregC & 0x01) << 0 + | (env->sregZ =3D=3D 0 ? 1 : 0) << 1 + | (env->sregN) << 2 + | (env->sregV) << 3 + | (env->sregS) << 4 + | (env->sregH) << 5 + | (env->sregT) << 6 + | (env->sregI) << 7; + return sreg; +} + +static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg) +{ + env->sregC =3D (sreg >> 0) & 0x01; + env->sregZ =3D (sreg >> 1) & 0x01 ? 0 : 1; + env->sregN =3D (sreg >> 2) & 0x01; + env->sregV =3D (sreg >> 3) & 0x01; + env->sregS =3D (sreg >> 4) & 0x01; + env->sregH =3D (sreg >> 5) & 0x01; + env->sregT =3D (sreg >> 6) & 0x01; + env->sregI =3D (sreg >> 7) & 0x01; +} + +#include "exec/exec-all.h" + +#endif /* !defined (CPU_AVR_H) */ diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c new file mode 100644 index 0000000000..537dc7226e --- /dev/null +++ b/target/avr/gdbstub.c @@ -0,0 +1,85 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int avr_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + /* R */ + if (n < 32) { + return gdb_get_reg8(mem_buf, env->r[n]); + } + + /* SREG */ + if (n =3D=3D 32) { + uint8_t sreg =3D cpu_get_sreg(env); + + return gdb_get_reg8(mem_buf, sreg); + } + + /* SP */ + if (n =3D=3D 33) { + return gdb_get_reg16(mem_buf, env->sp & 0x0000ffff); + } + + /* PC */ + if (n =3D=3D 34) { + return gdb_get_reg32(mem_buf, env->pc_w * 2); + } + + return 0; +} + +int avr_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + /* R */ + if (n < 32) { + env->r[n] =3D *mem_buf; + return 1; + } + + /* SREG */ + if (n =3D=3D 32) { + cpu_set_sreg(env, *mem_buf); + return 1; + } + + /* SP */ + if (n =3D=3D 33) { + env->sp =3D lduw_p(mem_buf); + return 2; + } + + /* PC */ + if (n =3D=3D 34) { + env->pc_w =3D ldl_p(mem_buf) / 2; + return 4; + } + + return 0; +} diff --git a/target/avr/machine.c b/target/avr/machine.c new file mode 100644 index 0000000000..438c8a6cb5 --- /dev/null +++ b/target/avr/machine.c @@ -0,0 +1,122 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "cpu.h" +#include "hw/boards.h" +#include "migration/qemu-file.h" + +static int get_sreg(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + CPUAVRState *env =3D opaque; + uint8_t sreg; + + sreg =3D qemu_get_byte(f); + cpu_set_sreg(env, sreg); + return 0; +} + +static int put_sreg( + QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + CPUAVRState *env =3D opaque; + uint8_t sreg =3D cpu_get_sreg(env); + + qemu_put_byte(f, sreg); + return 0; +} + +static const VMStateInfo vms_sreg =3D { + .name =3D "sreg", + .get =3D get_sreg, + .put =3D put_sreg, +}; + +static int get_segment( + QEMUFile *f, void *opaque, size_t size, const VMStateField *field) +{ + uint32_t *ramp =3D opaque; + uint8_t temp; + + temp =3D qemu_get_byte(f); + *ramp =3D ((uint32_t)temp) << 16; + return 0; +} + +static int put_segment( + QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + uint32_t *ramp =3D opaque; + uint8_t temp =3D *ramp >> 16; + + qemu_put_byte(f, temp); + return 0; +} + +static const VMStateInfo vms_rampD =3D { + .name =3D "rampD", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_rampX =3D { + .name =3D "rampX", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_rampY =3D { + .name =3D "rampY", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_rampZ =3D { + .name =3D "rampZ", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_eind =3D { + .name =3D "eind", + .get =3D get_segment, + .put =3D put_segment, +}; + +const VMStateDescription vms_avr_cpu =3D { + .name =3D "cpu", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(env.pc_w, AVRCPU), + VMSTATE_UINT32(env.sp, AVRCPU), + + VMSTATE_UINT32_ARRAY(env.r, AVRCPU, NO_CPU_REGISTERS), + + VMSTATE_SINGLE(env, AVRCPU, 0, vms_sreg, CPUAVRState), + VMSTATE_SINGLE(env.rampD, AVRCPU, 0, vms_rampD, uint32_t), + VMSTATE_SINGLE(env.rampX, AVRCPU, 0, vms_rampX, uint32_t), + VMSTATE_SINGLE(env.rampY, AVRCPU, 0, vms_rampY, uint32_t), + VMSTATE_SINGLE(env.rampZ, AVRCPU, 0, vms_rampZ, uint32_t), + VMSTATE_SINGLE(env.eind, AVRCPU, 0, vms_eind, uint32_t), + + VMSTATE_END_OF_LIST() + } +}; --=20 2.21.0 From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976339; cv=none; d=zoho.com; s=zohoarc; b=VeFTQaDedxygP17fZc94f6VhP5+50zo19ldlTQGcAua3qjaj8AGYlBSJduktq3V0DrT3S6sKczhPRHsNPDiT9ELWE+Eb5KPPLySPHVsCX16tmLx6X0m+HOVYpMoHwZZbvENExrBPJ7AB2M+SaCWmeQAcGL5gIvH3UcD75vgmul0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976339; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=SptCzoPEbEgKfi+VnphJOEFXEGTSs0IT1Bt3s82rKdE=; b=gIC3LauKHuOvxuH/bEOluuVxAFKo7uaXWUZ7MhSCvvCdZ54qyONS+Pmi7T1b1BLyoSLLRiBsEh4mGUn3hDAu+LyaOi5gcUfAZgvl3lVosNrvzOP3wdNKr1s+PnhO2xr6EhjAdtQ3QoA4vllEh0g296Q0XXLCvsdPByJknxhGodI= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (209.51.188.17 [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976339556348.4929923688155; Sat, 4 May 2019 06:25:39 -0700 (PDT) Received: from localhost ([127.0.0.1]:56624 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMufN-0006gP-Au for importer@patchew.org; Sat, 04 May 2019 09:25:25 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44358) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDP-0007XK-Ib for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDM-0002wO-Vy for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:15 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:48936) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002uc-NE for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:12 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDJ-000Jin-Go; Sat, 04 May 2019 09:40:09 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:34 +0100 Message-Id: <20190504083638.13380-5-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:02 -0400 Subject: [Qemu-devel] [PATCH v1 4/8] target/avr: Add instruction helpers X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Stubs for unimplemented instructions and helpers for instructions that need= to interact with QEMU. SPM and WDR are unimplemented because they require emulation of complex per= ipherals. The implementation of SLEEP is very limited due to the lack of peripherals = to generate wake interrupts. Memory access instructions are implemented here because some address ranges= actually refer to CPU registers. Signed-off-by: Sarah Harris --- target/avr/helper.c | 343 ++++++++++++++++++++++++++++++++++++++++++++ target/avr/helper.h | 28 ++++ 2 files changed, 371 insertions(+) create mode 100644 target/avr/helper.c create mode 100644 target/avr/helper.h diff --git a/target/avr/helper.c b/target/avr/helper.c new file mode 100644 index 0000000000..29b8bfce02 --- /dev/null +++ b/target/avr/helper.c @@ -0,0 +1,343 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "exec/ioport.h" +#include "qemu/host-utils.h" +#include "qemu/error-report.h" + +bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + bool ret =3D false; + CPUClass *cc =3D CPU_GET_CLASS(cs); + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + if (interrupt_request & CPU_INTERRUPT_RESET) { + if (cpu_interrupts_enabled(env)) { + cs->exception_index =3D EXCP_RESET; + cc->do_interrupt(cs); + + cs->interrupt_request &=3D ~CPU_INTERRUPT_RESET; + + ret =3D true; + } + } + if (interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_interrupts_enabled(env) && env->intsrc !=3D 0) { + int index =3D ctz32(env->intsrc); + cs->exception_index =3D EXCP_INT(index); + cc->do_interrupt(cs); + + env->intsrc &=3D env->intsrc - 1; /* clear the interrupt */ + cs->interrupt_request &=3D ~CPU_INTERRUPT_HARD; + + ret =3D true; + } + } + return ret; +} + +void avr_cpu_do_interrupt(CPUState *cs) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + uint32_t ret =3D env->pc_w; + int vector =3D 0; + int size =3D avr_feature(env, AVR_FEATURE_JMP_CALL) ? 2 : 1; + int base =3D 0; + + if (cs->exception_index =3D=3D EXCP_RESET) { + vector =3D 0; + } else if (env->intsrc !=3D 0) { + vector =3D ctz32(env->intsrc) + 1; + } + + if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) { + cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); + cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); + cpu_stb_data(env, env->sp--, (ret & 0xff0000) >> 16); + } else if (avr_feature(env, AVR_FEATURE_2_BYTE_PC)) { + cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); + cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); + } else { + cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); + } + + env->pc_w =3D base + vector * size; + env->sregI =3D 0; /* clear Global Interrupt Flag */ + + cs->exception_index =3D -1; +} + +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf, + int len, bool is_write) +{ + return cpu_memory_rw_debug(cs, addr, buf, len, is_write); +} + +hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + return addr; /* I assume 1:1 address correspondance */ +} + +int avr_cpu_handle_mmu_fault( + CPUState *cs, vaddr address, int size, int rw, int mmu_idx) +{ + /* currently it's assumed that this will never happen */ + cs->exception_index =3D EXCP_DEBUG; + cpu_dump_state(cs, stderr, 0); + return 1; +} + +void tlb_fill(CPUState *cs, target_ulong vaddr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) +{ + int prot =3D 0; + MemTxAttrs attrs =3D {}; + uint32_t paddr; + + vaddr &=3D TARGET_PAGE_MASK; + + if (mmu_idx =3D=3D MMU_CODE_IDX) { + /* access to code in flash */ + paddr =3D OFFSET_CODE + vaddr; + prot =3D PAGE_READ | PAGE_EXEC; + if (paddr + TARGET_PAGE_SIZE > OFFSET_DATA) { + error_report("execution left flash memory"); + exit(1); + } + } else if (vaddr < NO_CPU_REGISTERS + NO_IO_REGISTERS) { + /* + * access to CPU registers, exit and rebuilt this TB to use full a= ccess + * incase it touches specially handled registers like SREG or SP + */ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + env->fullacc =3D 1; + cpu_loop_exit_restore(cs, retaddr); + } else { + /* access to memory. nothing special */ + paddr =3D OFFSET_DATA + vaddr; + prot =3D PAGE_READ | PAGE_WRITE; + } + + tlb_set_page_with_attrs( + cs, vaddr, paddr, attrs, prot, mmu_idx, TARGET_PAGE_SIZE); +} + +void helper_sleep(CPUAVRState *env) +{ + CPUState *cs =3D CPU(avr_env_get_cpu(env)); + + cs->exception_index =3D EXCP_HLT; + cpu_loop_exit(cs); +} + +void helper_unsupported(CPUAVRState *env) +{ + CPUState *cs =3D CPU(avr_env_get_cpu(env)); + + /* + * I count not find what happens on the real platform, so + * it's EXCP_DEBUG for meanwhile + */ + cs->exception_index =3D EXCP_DEBUG; + if (qemu_loglevel_mask(LOG_UNIMP)) { + qemu_log("UNSUPPORTED\n"); + cpu_dump_state(cs, qemu_logfile, 0); + } + cpu_loop_exit(cs); +} + +void helper_debug(CPUAVRState *env) +{ + CPUState *cs =3D CPU(avr_env_get_cpu(env)); + + cs->exception_index =3D EXCP_DEBUG; + cpu_loop_exit(cs); +} + +void helper_wdr(CPUAVRState *env) +{ + CPUState *cs =3D CPU(avr_env_get_cpu(env)); + + /* WD is not implemented yet, placeholder */ + cs->exception_index =3D EXCP_DEBUG; + cpu_loop_exit(cs); +} + +/* + * This function implements IN instruction + * + * It does the following + * a. if an IO register belongs to CPU, its value is read and returned + * b. otherwise io address is translated to mem address and physical memo= ry + * is read. + * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation + * + */ +target_ulong helper_inb(CPUAVRState *env, uint32_t port) +{ + target_ulong data =3D 0; + + switch (port) { + case 0x38: /* RAMPD */ + data =3D 0xff & (env->rampD >> 16); + break; + case 0x39: /* RAMPX */ + data =3D 0xff & (env->rampX >> 16); + break; + case 0x3a: /* RAMPY */ + data =3D 0xff & (env->rampY >> 16); + break; + case 0x3b: /* RAMPZ */ + data =3D 0xff & (env->rampZ >> 16); + break; + case 0x3c: /* EIND */ + data =3D 0xff & (env->eind >> 16); + break; + case 0x3d: /* SPL */ + data =3D env->sp & 0x00ff; + break; + case 0x3e: /* SPH */ + data =3D env->sp >> 8; + break; + case 0x3f: /* SREG */ + data =3D cpu_get_sreg(env); + break; + default: + /* not a special register, pass to normal memory access */ + cpu_physical_memory_read(OFFSET_IO_REGISTERS + port, &data, 1); + } + + return data; +} + +/* + * This function implements OUT instruction + * + * It does the following + * a. if an IO register belongs to CPU, its value is written into the re= gister + * b. otherwise io address is translated to mem address and physical mem= ory + * is written. + * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementati= on + * + */ +void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data) +{ + data &=3D 0x000000ff; + + switch (port) { + case 0x38: /* RAMPD */ + if (avr_feature(env, AVR_FEATURE_RAMPD)) { + env->rampD =3D (data & 0xff) << 16; + } + break; + case 0x39: /* RAMPX */ + if (avr_feature(env, AVR_FEATURE_RAMPX)) { + env->rampX =3D (data & 0xff) << 16; + } + break; + case 0x3a: /* RAMPY */ + if (avr_feature(env, AVR_FEATURE_RAMPY)) { + env->rampY =3D (data & 0xff) << 16; + } + break; + case 0x3b: /* RAMPZ */ + if (avr_feature(env, AVR_FEATURE_RAMPZ)) { + env->rampZ =3D (data & 0xff) << 16; + } + break; + case 0x3c: /* EIDN */ + env->eind =3D (data & 0xff) << 16; + break; + case 0x3d: /* SPL */ + env->sp =3D (env->sp & 0xff00) | (data); + break; + case 0x3e: /* SPH */ + if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) { + env->sp =3D (env->sp & 0x00ff) | (data << 8); + } + break; + case 0x3f: /* SREG */ + cpu_set_sreg(env, data); + break; + default: + /* not a special register, pass to normal memory access */ + cpu_physical_memory_write(OFFSET_IO_REGISTERS + port, &data, 1); + } +} + +/* + * this function implements LD instruction when there is a posibility to = read + * from a CPU register + */ +target_ulong helper_fullrd(CPUAVRState *env, uint32_t addr) +{ + uint8_t data; + + env->fullacc =3D false; + + if (addr < NO_CPU_REGISTERS) { + /* CPU registers */ + data =3D env->r[addr]; + } else if (addr < NO_CPU_REGISTERS + NO_IO_REGISTERS) { + /* IO registers */ + data =3D helper_inb(env, addr - NO_CPU_REGISTERS); + } else { + /* memory */ + cpu_physical_memory_read(OFFSET_DATA + addr, &data, 1); + } + return data; +} + +/* + * this function implements ST instruction when there is a posibility to = write + * into a CPU register + */ +void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) +{ + env->fullacc =3D false; + + /* Following logic assumes this: */ + assert(OFFSET_CPU_REGISTERS =3D=3D OFFSET_DATA); + assert(OFFSET_IO_REGISTERS =3D=3D OFFSET_CPU_REGISTERS + NO_CPU_REGIST= ERS); + + if (addr < NO_CPU_REGISTERS) { + /* CPU registers */ + env->r[addr] =3D data; + } else if (addr < NO_CPU_REGISTERS + NO_IO_REGISTERS) { + /* IO registers */ + helper_outb(env, addr - NO_CPU_REGISTERS, data); + } else { + /* memory */ + cpu_physical_memory_write(OFFSET_DATA + addr, &data, 1); + } +} diff --git a/target/avr/helper.h b/target/avr/helper.h new file mode 100644 index 0000000000..603631520a --- /dev/null +++ b/target/avr/helper.h @@ -0,0 +1,28 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +DEF_HELPER_1(wdr, void, env) +DEF_HELPER_1(debug, void, env) +DEF_HELPER_1(sleep, void, env) +DEF_HELPER_1(unsupported, void, env) +DEF_HELPER_3(outb, void, env, i32, i32) +DEF_HELPER_2(inb, tl, env, i32) +DEF_HELPER_3(fullwr, void, env, i32, i32) +DEF_HELPER_2(fullrd, tl, env, i32) --=20 2.21.0 From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976469; cv=none; d=zoho.com; s=zohoarc; b=XOlOvK87KvNiceItfPul5CgaWzaG40sXGbgRcBDAXwB3vFiqvF1nAR7ZB4591gze/OHi5lsMzHgibbxhSWR87GaBTTSPNp49EcI2f8QrfeRwKvNZVqhHD0bHA2tW2ggvbWPtJPGu4cnkC+1ZDKDmHHqAGwVtqTKxi8iFXXN4NkI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976469; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=p5AYiCIuBgXgbc/c5HQmC9OAFbM1h949n/qd9UP0ejY=; b=Tqio8B+tXqnH0qJfaXG7+iROTl9pB+plAwbr9dSSgTOgmT5W9UAxB5SReh4yfctxh/bgnWFZZeVlIVSyfbDdm3+WxS4rF1Oklx/8VHRYR8CgpLL7EM23D5eLC8RC0pobg9I8r8/J7kyJB04sYhir3n0HHwLEDOvzku4oI0D+qhI= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976469517637.5069832055166; Sat, 4 May 2019 06:27:49 -0700 (PDT) Received: from localhost ([127.0.0.1]:56674 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMuhd-0000ve-Ru for importer@patchew.org; Sat, 04 May 2019 09:27:45 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44435) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDX-0007cN-PK for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDN-0002ws-8B for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:23 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:47793) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002ue-NJ for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:13 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDJ-000Jin-NS; Sat, 04 May 2019 09:40:09 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:35 +0100 Message-Id: <20190504083638.13380-6-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:02 -0400 Subject: [Qemu-devel] [PATCH v1 5/8] target/avr: Add instruction translation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" This includes: - table of instruction bit patterns - intermediate translation functions that extract parameters from opcodes - TCG translations for each instruction Signed-off-by: Sarah Harris --- target/avr/translate-inst.h | 695 ++++++++ target/avr/translate.c | 3013 +++++++++++++++++++++++++++++++++++ 2 files changed, 3708 insertions(+) create mode 100644 target/avr/translate-inst.h create mode 100644 target/avr/translate.c diff --git a/target/avr/translate-inst.h b/target/avr/translate-inst.h new file mode 100644 index 0000000000..e27f403ba7 --- /dev/null +++ b/target/avr/translate-inst.h @@ -0,0 +1,695 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +/* + * Functions for extracting instruction parameters from raw opcodes. + */ + +#ifndef AVR_TRANSLATE_INST_H_ +#define AVR_TRANSLATE_INST_H_ + +static inline uint32_t MOVW_Rr(uint32_t opcode) +{ + return extract32(opcode, 0, 4); +} + +static inline uint32_t MOVW_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t MULS_Rr(uint32_t opcode) +{ + return extract32(opcode, 0, 4); +} + +static inline uint32_t MULS_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t MULSU_Rr(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t MULSU_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 3); +} + +static inline uint32_t FMUL_Rr(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t FMUL_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 3); +} + +static inline uint32_t FMULS_Rr(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t FMULS_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 3); +} + +static inline uint32_t FMULSU_Rr(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t FMULSU_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 3); +} + +static inline uint32_t CPC_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t CPC_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t SBC_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t SBC_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t ADD_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t ADD_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t AND_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t AND_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t EOR_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t EOR_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t OR_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t OR_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t MOV_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t MOV_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t CPSE_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t CPSE_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t CP_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t CP_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t SUB_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t SUB_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t ADC_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t ADC_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t CPI_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t CPI_Imm(uint32_t opcode) +{ + return (extract32(opcode, 8, 4) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t SBCI_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t SBCI_Imm(uint32_t opcode) +{ + return (extract32(opcode, 8, 4) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t ORI_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t ORI_Imm(uint32_t opcode) +{ + return (extract32(opcode, 8, 4) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t SUBI_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t SUBI_Imm(uint32_t opcode) +{ + return (extract32(opcode, 8, 4) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t ANDI_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t ANDI_Imm(uint32_t opcode) +{ + return (extract32(opcode, 8, 4) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t LDDZ_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDDZ_Imm(uint32_t opcode) +{ + return (extract32(opcode, 13, 1) << 5) | + (extract32(opcode, 10, 2) << 3) | + (extract32(opcode, 0, 3)); +} + +static inline uint32_t LDDY_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDDY_Imm(uint32_t opcode) +{ + return (extract32(opcode, 13, 1) << 5) | + (extract32(opcode, 10, 2) << 3) | + (extract32(opcode, 0, 3)); +} + +static inline uint32_t STDZ_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STDZ_Imm(uint32_t opcode) +{ + return (extract32(opcode, 13, 1) << 5) | + (extract32(opcode, 10, 2) << 3) | + (extract32(opcode, 0, 3)); +} + +static inline uint32_t STDY_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STDY_Imm(uint32_t opcode) +{ + return (extract32(opcode, 13, 1) << 5) | + (extract32(opcode, 10, 2) << 3) | + (extract32(opcode, 0, 3)); +} + +static inline uint32_t LDS_Imm(uint32_t opcode) +{ + return extract32(opcode, 0, 16); +} + +static inline uint32_t LDS_Rd(uint32_t opcode) +{ + return extract32(opcode, 20, 5); +} + +static inline uint32_t LDZ2_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDZ3_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LPM2_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LPMX_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t ELPM2_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t ELPMX_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDY2_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDY3_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDX1_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDX2_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LDX3_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t POP_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STS_Imm(uint32_t opcode) +{ + return extract32(opcode, 0, 16); +} + +static inline uint32_t STS_Rd(uint32_t opcode) +{ + return extract32(opcode, 20, 5); +} + +static inline uint32_t STZ2_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STZ3_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t XCH_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LAS_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LAC_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LAT_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STY2_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STY3_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STX1_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STX2_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t STX3_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t PUSH_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t COM_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t NEG_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t SWAP_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t INC_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t ASR_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t LSR_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t ROR_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t BSET_Bit(uint32_t opcode) +{ + return extract32(opcode, 4, 3); +} + +static inline uint32_t BCLR_Bit(uint32_t opcode) +{ + return extract32(opcode, 4, 3); +} + +static inline uint32_t DEC_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t DES_Imm(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t JMP_Imm(uint32_t opcode) +{ + return (extract32(opcode, 20, 5) << 17) | + (extract32(opcode, 0, 17)); +} + +static inline uint32_t CALL_Imm(uint32_t opcode) +{ + return (extract32(opcode, 20, 5) << 17) | + (extract32(opcode, 0, 17)); +} + +static inline uint32_t ADIW_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 2); +} + +static inline uint32_t ADIW_Imm(uint32_t opcode) +{ + return (extract32(opcode, 6, 2) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t SBIW_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 2); +} + +static inline uint32_t SBIW_Imm(uint32_t opcode) +{ + return (extract32(opcode, 6, 2) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t CBI_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t CBI_Imm(uint32_t opcode) +{ + return extract32(opcode, 3, 5); +} + +static inline uint32_t SBIC_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t SBIC_Imm(uint32_t opcode) +{ + return extract32(opcode, 3, 5); +} + +static inline uint32_t SBI_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t SBI_Imm(uint32_t opcode) +{ + return extract32(opcode, 3, 5); +} + +static inline uint32_t SBIS_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t SBIS_Imm(uint32_t opcode) +{ + return extract32(opcode, 3, 5); +} + +static inline uint32_t MUL_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t MUL_Rr(uint32_t opcode) +{ + return (extract32(opcode, 9, 1) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t IN_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t IN_Imm(uint32_t opcode) +{ + return (extract32(opcode, 9, 2) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t OUT_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t OUT_Imm(uint32_t opcode) +{ + return (extract32(opcode, 9, 2) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t RJMP_Imm(uint32_t opcode) +{ + return extract32(opcode, 0, 12); +} + +static inline uint32_t LDI_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 4); +} + +static inline uint32_t LDI_Imm(uint32_t opcode) +{ + return (extract32(opcode, 8, 4) << 4) | + (extract32(opcode, 0, 4)); +} + +static inline uint32_t RCALL_Imm(uint32_t opcode) +{ + return extract32(opcode, 0, 12); +} + +static inline uint32_t BRBS_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t BRBS_Imm(uint32_t opcode) +{ + return extract32(opcode, 3, 7); +} + +static inline uint32_t BRBC_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t BRBC_Imm(uint32_t opcode) +{ + return extract32(opcode, 3, 7); +} + +static inline uint32_t BLD_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t BLD_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t BST_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t BST_Rd(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t SBRC_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t SBRC_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +static inline uint32_t SBRS_Bit(uint32_t opcode) +{ + return extract32(opcode, 0, 3); +} + +static inline uint32_t SBRS_Rr(uint32_t opcode) +{ + return extract32(opcode, 4, 5); +} + +#endif diff --git a/target/avr/translate.c b/target/avr/translate.c new file mode 100644 index 0000000000..3be3e258f9 --- /dev/null +++ b/target/avr/translate.c @@ -0,0 +1,3013 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "qemu/qemu-print.h" +#include "tcg/tcg.h" +#include "cpu.h" +#include "decode.h" +#include "exec/exec-all.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" +#include "exec/gdbstub.h" + +static TCGv cpu_pc; + +static TCGv cpu_Cf; +static TCGv cpu_Zf; +static TCGv cpu_Nf; +static TCGv cpu_Vf; +static TCGv cpu_Sf; +static TCGv cpu_Hf; +static TCGv cpu_Tf; +static TCGv cpu_If; + +static TCGv cpu_rampD; +static TCGv cpu_rampX; +static TCGv cpu_rampY; +static TCGv cpu_rampZ; + +static TCGv cpu_r[32]; +static TCGv cpu_eind; +static TCGv cpu_sp; + +#define REG(x) (cpu_r[x]) + +enum { + BS_NONE =3D 0, /* Nothing special (none of the below) */ + BS_STOP =3D 1, /* We want to stop translation for any reason */ + BS_BRANCH =3D 2, /* A branch condition is reached */ + BS_EXCP =3D 3, /* An exception condition is reached */ +}; + +typedef struct DisasContext DisasContext; +typedef struct InstInfo InstInfo; + +struct InstInfo { + target_long cpc; + target_long npc; + uint32_t opcode; + TranslateFn translate; + unsigned length; +}; + +/* This is the state at translation time. */ +struct DisasContext { + struct TranslationBlock *tb; + CPUAVRState *env; + + InstInfo inst[2];/* two consecutive instructions */ + + /* Routine used to access memory */ + int memidx; + int bstate; + int singlestep; +}; + +static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +{ + TranslationBlock *tb =3D ctx->tb; + + if (ctx->singlestep =3D=3D 0) { + tcg_gen_goto_tb(n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb(tb, n); + } else { + tcg_gen_movi_i32(cpu_pc, dest); + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(NULL, 0); + } +} + +#include "exec/gen-icount.h" +#include "translate-inst.h" + +static void gen_add_CHf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + TCGv t3 =3D tcg_temp_new_i32(); + + tcg_gen_and_tl(t1, Rd, Rr); /* t1 =3D Rd & Rr */ + tcg_gen_andc_tl(t2, Rd, R); /* t2 =3D Rd & ~R */ + tcg_gen_andc_tl(t3, Rr, R); /* t3 =3D Rr & ~R */ + tcg_gen_or_tl(t1, t1, t2); /* t1 =3D t1 | t2 | t3 */ + tcg_gen_or_tl(t1, t1, t3); + + tcg_gen_shri_tl(cpu_Cf, t1, 7); /* Cf =3D t1(7) */ + tcg_gen_shri_tl(cpu_Hf, t1, 3); /* Hf =3D t1(3) */ + tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_add_Vf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + + /* t1 =3D Rd & Rr & ~R | ~Rd & ~Rr & R =3D (Rd ^ R) & ~(Rd ^ Rr) */ + tcg_gen_xor_tl(t1, Rd, R); + tcg_gen_xor_tl(t2, Rd, Rr); + tcg_gen_andc_tl(t1, t1, t2); + + tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf =3D t1(7) */ + + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_sub_CHf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + TCGv t3 =3D tcg_temp_new_i32(); + + /* Cf & Hf */ + tcg_gen_not_tl(t1, Rd); /* t1 =3D ~Rd */ + tcg_gen_and_tl(t2, t1, Rr); /* t2 =3D ~Rd & Rr */ + tcg_gen_or_tl(t3, t1, Rr); /* t3 =3D (~Rd | Rr) & R */ + tcg_gen_and_tl(t3, t3, R); + tcg_gen_or_tl(t2, t2, t3); /* t2 =3D ~Rd & Rr | ~Rd & R | R & Rr */ + tcg_gen_shri_tl(cpu_Cf, t2, 7); /* Cf =3D t2(7) */ + tcg_gen_shri_tl(cpu_Hf, t2, 3); /* Hf =3D t2(3) */ + tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_sub_Vf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + + /* Vf */ + /* t1 =3D Rd & ~Rr & ~R | ~Rd & Rr & R =3D (Rd ^ R) & (Rd ^ R) */ + tcg_gen_xor_tl(t1, Rd, R); + tcg_gen_xor_tl(t2, Rd, Rr); + tcg_gen_and_tl(t1, t1, t2); + tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf =3D t1(7) */ + + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_rshift_ZNVSf(TCGv R) +{ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf =3D R(7) */ + tcg_gen_xor_tl(cpu_Vf, cpu_Nf, cpu_Cf); + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ +} + +static void gen_NSf(TCGv R) +{ + tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf =3D R(7) */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ +} + +static void gen_ZNSf(TCGv R) +{ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf =3D R(7) */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ +} + +static void gen_push_ret(DisasContext *ctx, int ret) +{ + if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) { + + TCGv t0 =3D tcg_const_i32((ret & 0x0000ff)); + + tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_UB); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + tcg_temp_free_i32(t0); + } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) { + + TCGv t0 =3D tcg_const_i32((ret & 0x00ffff)); + + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_BEUW); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + tcg_temp_free_i32(t0); + + } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) { + + TCGv lo =3D tcg_const_i32((ret & 0x0000ff)); + TCGv hi =3D tcg_const_i32((ret & 0xffff00) >> 8); + + tcg_gen_qemu_st_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 2); + tcg_gen_qemu_st_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + tcg_temp_free_i32(lo); + tcg_temp_free_i32(hi); + } +} + +static void gen_pop_ret(DisasContext *ctx, TCGv ret) +{ + if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) { + + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_UB); + + } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) { + + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_BEUW); + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + + } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) { + + TCGv lo =3D tcg_temp_new_i32(); + TCGv hi =3D tcg_temp_new_i32(); + + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_ld_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW); + + tcg_gen_addi_tl(cpu_sp, cpu_sp, 2); + tcg_gen_qemu_ld_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB); + + tcg_gen_deposit_tl(ret, lo, hi, 8, 16); + + tcg_temp_free_i32(lo); + tcg_temp_free_i32(hi); + } +} + +static void gen_jmp_ez(void) +{ + tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8); + tcg_gen_or_tl(cpu_pc, cpu_pc, cpu_eind); + tcg_gen_exit_tb(NULL, 0); +} + +static void gen_jmp_z(void) +{ + tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8); + tcg_gen_exit_tb(NULL, 0); +} + +/* + * in the gen_set_addr & gen_get_addr functions + * H assumed to be in 0x00ff0000 format + * M assumed to be in 0x000000ff format + * L assumed to be in 0x000000ff format + */ +static void gen_set_addr(TCGv addr, TCGv H, TCGv M, TCGv L) +{ + + tcg_gen_andi_tl(L, addr, 0x000000ff); + + tcg_gen_andi_tl(M, addr, 0x0000ff00); + tcg_gen_shri_tl(M, M, 8); + + tcg_gen_andi_tl(H, addr, 0x00ff0000); +} + +static void gen_set_xaddr(TCGv addr) +{ + gen_set_addr(addr, cpu_rampX, cpu_r[27], cpu_r[26]); +} + +static void gen_set_yaddr(TCGv addr) +{ + gen_set_addr(addr, cpu_rampY, cpu_r[29], cpu_r[28]); +} + +static void gen_set_zaddr(TCGv addr) +{ + gen_set_addr(addr, cpu_rampZ, cpu_r[31], cpu_r[30]); +} + +static TCGv gen_get_addr(TCGv H, TCGv M, TCGv L) +{ + TCGv addr =3D tcg_temp_new_i32(); + + tcg_gen_deposit_tl(addr, M, H, 8, 8); + tcg_gen_deposit_tl(addr, L, addr, 8, 16); + + return addr; +} + +static TCGv gen_get_xaddr(void) +{ + return gen_get_addr(cpu_rampX, cpu_r[27], cpu_r[26]); +} + +static TCGv gen_get_yaddr(void) +{ + return gen_get_addr(cpu_rampY, cpu_r[29], cpu_r[28]); +} + +static TCGv gen_get_zaddr(void) +{ + return gen_get_addr(cpu_rampZ, cpu_r[31], cpu_r[30]); +} + +/* + * Adds two registers and the contents of the C Flag and places the resul= t in + * the destination register Rd. + */ +static int translate_ADC(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[ADC_Rd(opcode)]; + TCGv Rr =3D cpu_r[ADC_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_add_tl(R, Rd, Rr); /* R =3D Rd + Rr + Cf */ + tcg_gen_add_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_add_CHf(R, Rd, Rr); + gen_add_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Adds two registers without the C Flag and places the result in the + * destination register Rd. + */ +static int translate_ADD(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[ADD_Rd(opcode)]; + TCGv Rr =3D cpu_r[ADD_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_add_tl(R, Rd, Rr); /* Rd =3D Rd + Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_add_CHf(R, Rd, Rr); + gen_add_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Adds an immediate value (0 - 63) to a register pair and places the res= ult + * in the register pair. This instruction operates on the upper four regi= ster + * pairs, and is well suited for operations on the pointer registers. Th= is + * instruction is not available in all devices. Refer to the device speci= fic + * instruction set summary. + */ +static int translate_ADIW(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_ADIW_SBIW) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv RdL =3D cpu_r[24 + 2 * ADIW_Rd(opcode)]; + TCGv RdH =3D cpu_r[25 + 2 * ADIW_Rd(opcode)]; + int Imm =3D (ADIW_Imm(opcode)); + TCGv R =3D tcg_temp_new_i32(); + TCGv Rd =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd =3D RdH:RdL */ + tcg_gen_addi_tl(R, Rd, Imm); /* R =3D Rd + Imm */ + tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */ + + /* Cf */ + tcg_gen_andc_tl(cpu_Cf, Rd, R); /* Cf =3D Rd & ~R */ + tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15); + + /* Vf */ + tcg_gen_andc_tl(cpu_Vf, R, Rd); /* Vf =3D R & ~Rd */ + tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15); + + /* Zf */ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + + /* Nf */ + tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf =3D R(15) */ + + /* Sf */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf);/* Sf =3D Nf ^ Vf */ + + /* R */ + tcg_gen_andi_tl(RdL, R, 0xff); + tcg_gen_shri_tl(RdH, R, 8); + + tcg_temp_free_i32(Rd); + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Performs the logical AND between the contents of register Rd and regis= ter + * Rr and places the result in the destination register Rd. + */ +static int translate_AND(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[AND_Rd(opcode)]; + TCGv Rr =3D cpu_r[AND_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_and_tl(R, Rd, Rr); /* Rd =3D Rd and Rr */ + + /* Vf */ + tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf =3D 0 */ + + /* Zf */ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Performs the logical AND between the contents of register Rd and a con= stant + * and places the result in the destination register Rd. + */ +static int translate_ANDI(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[16 + ANDI_Rd(opcode)]; + int Imm =3D (ANDI_Imm(opcode)); + + /* op */ + tcg_gen_andi_tl(Rd, Rd, Imm); /* Rd =3D Rd & Imm */ + + tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf =3D 0 */ + gen_ZNSf(Rd); + + return BS_NONE; +} + +/* + * Shifts all bits in Rd one place to the right. Bit 7 is held constant. = Bit 0 + * is loaded into the C Flag of the SREG. This operation effectively divi= des a + * signed value by two without changing its sign. The Carry Flag can be u= sed to + * round the result. + */ +static int translate_ASR(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[ASR_Rd(opcode)]; + TCGv t0 =3D tcg_temp_new_i32(); + + /* Cf */ + tcg_gen_andi_tl(cpu_Cf, Rd, 1); /* Cf =3D Rd(0) */ + + /* op */ + tcg_gen_andi_tl(t0, Rd, 0x80); /* Rd =3D (Rd & 0x80) | (Rd >> 1) */ + tcg_gen_shri_tl(Rd, Rd, 1); + tcg_gen_or_tl(Rd, Rd, t0); + + gen_rshift_ZNVSf(Rd); + + tcg_temp_free_i32(t0); + + return BS_NONE; +} + +/* + * Clears a single Flag in SREG. + */ +static int translate_BCLR(DisasContext *ctx, uint32_t opcode) +{ + switch (BCLR_Bit(opcode)) { + case 0x00: + tcg_gen_movi_tl(cpu_Cf, 0x00); + break; + case 0x01: + tcg_gen_movi_tl(cpu_Zf, 0x01); + break; + case 0x02: + tcg_gen_movi_tl(cpu_Nf, 0x00); + break; + case 0x03: + tcg_gen_movi_tl(cpu_Vf, 0x00); + break; + case 0x04: + tcg_gen_movi_tl(cpu_Sf, 0x00); + break; + case 0x05: + tcg_gen_movi_tl(cpu_Hf, 0x00); + break; + case 0x06: + tcg_gen_movi_tl(cpu_Tf, 0x00); + break; + case 0x07: + tcg_gen_movi_tl(cpu_If, 0x00); + break; + } + + return BS_NONE; +} + +/* + * Copies the T Flag in the SREG (Status Register) to bit b in register R= d. + */ +static int translate_BLD(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[BLD_Rd(opcode)]; + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_andi_tl(Rd, Rd, ~(1u << BLD_Bit(opcode))); /* clear bit */ + tcg_gen_shli_tl(t1, cpu_Tf, BLD_Bit(opcode)); /* create mask */ + tcg_gen_or_tl(Rd, Rd, t1); + + tcg_temp_free_i32(t1); + + return BS_NONE; +} + +/* + * Conditional relative branch. Tests a single bit in SREG and branches + * relatively to PC if the bit is cleared. This instruction branches rela= tively + * to PC in either direction (PC - 63 < =3D destination <=3D PC + 64). The + * parameter k is the offset from PC and is represented in two's compleme= nt + * form. + */ +static int translate_BRBC(DisasContext *ctx, uint32_t opcode) +{ + TCGLabel *taken =3D gen_new_label(); + int Imm =3D sextract32(BRBC_Imm(opcode), 0, 7); + + switch (BRBC_Bit(opcode)) { + case 0x00: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Cf, 0, taken); + break; + case 0x01: + tcg_gen_brcondi_i32(TCG_COND_NE, cpu_Zf, 0, taken); + break; + case 0x02: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Nf, 0, taken); + break; + case 0x03: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Vf, 0, taken); + break; + case 0x04: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Sf, 0, taken); + break; + case 0x05: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Hf, 0, taken); + break; + case 0x06: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Tf, 0, taken); + break; + case 0x07: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_If, 0, taken); + break; + } + + gen_goto_tb(ctx, 1, ctx->inst[0].npc); + gen_set_label(taken); + gen_goto_tb(ctx, 0, ctx->inst[0].npc + Imm); + + return BS_BRANCH; +} + +/* + * Conditional relative branch. Tests a single bit in SREG and branches + * relatively to PC if the bit is set. This instruction branches relative= ly to + * PC in either direction (PC - 63 < =3D destination <=3D PC + 64). The p= arameter k + * is the offset from PC and is represented in two's complement form. + */ +static int translate_BRBS(DisasContext *ctx, uint32_t opcode) +{ + TCGLabel *taken =3D gen_new_label(); + int Imm =3D sextract32(BRBS_Imm(opcode), 0, 7); + + switch (BRBS_Bit(opcode)) { + case 0x00: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Cf, 1, taken); + break; + case 0x01: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Zf, 0, taken); + break; + case 0x02: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Nf, 1, taken); + break; + case 0x03: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Vf, 1, taken); + break; + case 0x04: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Sf, 1, taken); + break; + case 0x05: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Hf, 1, taken); + break; + case 0x06: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_Tf, 1, taken); + break; + case 0x07: + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_If, 1, taken); + break; + } + + gen_goto_tb(ctx, 1, ctx->inst[0].npc); + gen_set_label(taken); + gen_goto_tb(ctx, 0, ctx->inst[0].npc + Imm); + + return BS_BRANCH; +} + +/* + * Sets a single Flag or bit in SREG. + */ +static int translate_BSET(DisasContext *ctx, uint32_t opcode) +{ + switch (BSET_Bit(opcode)) { + case 0x00: + tcg_gen_movi_tl(cpu_Cf, 0x01); + break; + case 0x01: + tcg_gen_movi_tl(cpu_Zf, 0x00); + break; + case 0x02: + tcg_gen_movi_tl(cpu_Nf, 0x01); + break; + case 0x03: + tcg_gen_movi_tl(cpu_Vf, 0x01); + break; + case 0x04: + tcg_gen_movi_tl(cpu_Sf, 0x01); + break; + case 0x05: + tcg_gen_movi_tl(cpu_Hf, 0x01); + break; + case 0x06: + tcg_gen_movi_tl(cpu_Tf, 0x01); + break; + case 0x07: + tcg_gen_movi_tl(cpu_If, 0x01); + break; + } + + return BS_NONE; +} + +/* + * The BREAK instruction is used by the On-chip Debug system, and is + * normally not used in the application software. When the BREAK instruct= ion is + * executed, the AVR CPU is set in the Stopped Mode. This gives the On-ch= ip + * Debugger access to internal resources. If any Lock bits are set, or e= ither + * the JTAGEN or OCDEN Fuses are unprogrammed, the CPU will treat the BRE= AK + * instruction as a NOP and will not enter the Stopped mode. This instru= ction + * is not available in all devices. Refer to the device specific instruct= ion + * set summary. + */ +static int translate_BREAK(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_BREAK) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + if (gdb_is_active()) { + /* Program counter is set to *current* instruction to mimic AVaRIC= E. */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[0].cpc); + gen_helper_debug(cpu_env); + } + + return BS_NONE; +} + +/* + * Stores bit b from Rd to the T Flag in SREG (Status Register). + */ +static int translate_BST(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[BST_Rd(opcode)]; + + tcg_gen_andi_tl(cpu_Tf, Rd, 1 << BST_Bit(opcode)); + tcg_gen_shri_tl(cpu_Tf, cpu_Tf, BST_Bit(opcode)); + + return BS_NONE; +} + +/* + * Calls to a subroutine within the entire Program memory. The return + * address (to the instruction after the CALL) will be stored onto the St= ack. + * (See also RCALL). The Stack Pointer uses a post-decrement scheme during + * CALL. This instruction is not available in all devices. Refer to the = device + * specific instruction set summary. + */ +static int translate_CALL(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_JMP_CALL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + int Imm =3D CALL_Imm(opcode); + int ret =3D ctx->inst[0].npc; + + gen_push_ret(ctx, ret); + gen_goto_tb(ctx, 0, Imm); + + return BS_BRANCH; +} + +/* + * Clears a specified bit in an I/O Register. This instruction operates on + * the lower 32 I/O Registers -- addresses 0-31. + */ +static int translate_CBI(DisasContext *ctx, uint32_t opcode) +{ + TCGv data =3D tcg_temp_new_i32(); + TCGv port =3D tcg_const_i32(CBI_Imm(opcode)); + + gen_helper_inb(data, cpu_env, port); + tcg_gen_andi_tl(data, data, ~(1 << CBI_Bit(opcode))); + gen_helper_outb(cpu_env, port, data); + + tcg_temp_free_i32(data); + tcg_temp_free_i32(port); + + return BS_NONE; +} + +/* + * Clears the specified bits in register Rd. Performs the logical AND + * between the contents of register Rd and the complement of the constant= mask + * K. The result will be placed in register Rd. + */ +static int translate_COM(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[COM_Rd(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_xori_tl(Rd, Rd, 0xff); + + tcg_gen_movi_tl(cpu_Cf, 1); /* Cf =3D 1 */ + tcg_gen_movi_tl(cpu_Vf, 0); /* Vf =3D 0 */ + gen_ZNSf(Rd); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs a compare between two registers Rd and Rr. + * None of the registers are changed. All conditional branches can be used + * after this instruction. + */ +static int translate_CP(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[CP_Rd(opcode)]; + TCGv Rr =3D cpu_r[CP_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs a compare between two registers Rd and Rr and + * also takes into account the previous carry. None of the registers are + * changed. All conditional branches can be used after this instruction. + */ +static int translate_CPC(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[CPC_Rd(opcode)]; + TCGv Rr =3D cpu_r[CPC_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr - Cf */ + tcg_gen_sub_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_NSf(R); + + /* + * Previous value remains unchanged when the result is zero; + * cleared otherwise. + */ + tcg_gen_or_tl(cpu_Zf, cpu_Zf, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs a compare between register Rd and a constant. + * The register is not changed. All conditional branches can be used afte= r this + * instruction. + */ +static int translate_CPI(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[16 + CPI_Rd(opcode)]; + int Imm =3D CPI_Imm(opcode); + TCGv Rr =3D tcg_const_i32(Imm); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + tcg_temp_free_i32(R); + tcg_temp_free_i32(Rr); + + return BS_NONE; +} + +/* + * This instruction performs a compare between two registers Rd and Rr, a= nd + * skips the next instruction if Rd =3D Rr. + */ +static int translate_CPSE(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[CPSE_Rd(opcode)]; + TCGv Rr =3D cpu_r[CPSE_Rr(opcode)]; + TCGLabel *skip =3D gen_new_label(); + + /* PC if next inst is skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[1].npc); + tcg_gen_brcond_i32(TCG_COND_EQ, Rd, Rr, skip); + /* PC if next inst is not skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[0].npc); + gen_set_label(skip); + + return BS_BRANCH; +} + +/* + * Subtracts one -1- from the contents of register Rd and places the resu= lt + * in the destination register Rd. The C Flag in SREG is not affected by= the + * operation, thus allowing the DEC instruction to be used on a loop coun= ter in + * multiple-precision computations. When operating on unsigned values, o= nly + * BREQ and BRNE branches can be expected to perform consistently. When + * operating on two's complement values, all signed branches are availabl= e. + */ +static int translate_DEC(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[DEC_Rd(opcode)]; + + tcg_gen_subi_tl(Rd, Rd, 1); /* Rd =3D Rd - 1 */ + tcg_gen_andi_tl(Rd, Rd, 0xff); /* make it 8 bits */ + + /* cpu_Vf =3D Rd =3D=3D 0x7f */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x7f); + gen_ZNSf(Rd); + + return BS_NONE; +} + +/* + * The module is an instruction set extension to the AVR CPU, performing + * DES iterations. The 64-bit data block (plaintext or ciphertext) is pla= ced in + * the CPU register file, registers R0-R7, where LSB of data is placed in= LSB + * of R0 and MSB of data is placed in MSB of R7. The full 64-bit key (inc= luding + * parity bits) is placed in registers R8- R15, organized in the register= file + * with LSB of key in LSB of R8 and MSB of key in MSB of R15. Executing o= ne DES + * instruction performs one round in the DES algorithm. Sixteen rounds mu= st be + * executed in increasing order to form the correct DES ciphertext or + * plaintext. Intermediate results are stored in the register file (R0-R1= 5) + * after each DES instruction. The instruction's operand (K) determines w= hich + * round is executed, and the half carry flag (H) determines whether encr= yption + * or decryption is performed. The DES algorithm is described in + * "Specifications for the Data Encryption Standard" (Federal Information + * Processing Standards Publication 46). Intermediate results in this + * implementation differ from the standard because the initial permutatio= n and + * the inverse initial permutation are performed each iteration. This doe= s not + * affect the result in the final ciphertext or plaintext, but reduces + * execution time. + */ +static int translate_DES(DisasContext *ctx, uint32_t opcode) +{ + /* TODO */ + if (avr_feature(ctx->env, AVR_FEATURE_DES) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + return BS_NONE; +} + +/* + * Indirect call of a subroutine pointed to by the Z (16 bits) Pointer + * Register in the Register File and the EIND Register in the I/O space. = This + * instruction allows for indirect calls to the entire 4M (words) Program + * memory space. See also ICALL. The Stack Pointer uses a post-decrement = scheme + * during EICALL. This instruction is not available in all devices. Refe= r to + * the device specific instruction set summary. + */ +static int translate_EICALL(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_EIJMP_EICALL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + int ret =3D ctx->inst[0].npc; + + gen_push_ret(ctx, ret); + + gen_jmp_ez(); + + return BS_BRANCH; +} + +/* + * Indirect jump to the address pointed to by the Z (16 bits) Pointer + * Register in the Register File and the EIND Register in the I/O space. = This + * instruction allows for indirect jumps to the entire 4M (words) Program + * memory space. See also IJMP. This instruction is not available in all + * devices. Refer to the device specific instruction set summary. + */ +static int translate_EIJMP(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_EIJMP_EICALL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + gen_jmp_ez(); + + return BS_BRANCH; +} + +/* + * Loads one byte pointed to by the Z-register and the RAMPZ Register in + * the I/O space, and places this byte in the destination register Rd. Th= is + * instruction features a 100% space effective constant initialization or + * constant data fetch. The Program memory is organized in 16-bit words w= hile + * the Z-pointer is a byte address. Thus, the least significant bit of the + * Z-pointer selects either low byte (ZLSB =3D 0) or high byte (ZLSB =3D = 1). This + * instruction can address the entire Program memory space. The Z-pointer + * Register can either be left unchanged by the operation, or it can be + * incremented. The incrementation applies to the entire 24-bit concatena= tion + * of the RAMPZ and Z-pointer Registers. Devices with Self-Programming + * capability can use the ELPM instruction to read the Fuse and Lock bit = value. + * Refer to the device documentation for a detailed description. This + * instruction is not available in all devices. Refer to the device speci= fic + * instruction set summary. + */ +static int translate_ELPM1(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_ELPM) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rd =3D cpu_r[0]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_ELPM2(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_ELPM) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rd =3D cpu_r[ELPM2_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_ELPMX(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_ELPMX) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rd =3D cpu_r[ELPMX_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Performs the logical EOR between the contents of register Rd and + * register Rr and places the result in the destination register Rd. + */ +static int translate_EOR(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[EOR_Rd(opcode)]; + TCGv Rr =3D cpu_r[EOR_Rr(opcode)]; + + tcg_gen_xor_tl(Rd, Rd, Rr); + + tcg_gen_movi_tl(cpu_Vf, 0); + gen_ZNSf(Rd); + + return BS_NONE; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit unsigned + * multiplication and shifts the result one bit left. + */ +static int translate_FMUL(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_MUL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[16 + FMUL_Rd(opcode)]; + TCGv Rr =3D cpu_r[16 + FMUL_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_mul_tl(R, Rd, Rr); /* R =3D Rd *Rr */ + tcg_gen_shli_tl(R, R, 1); + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + tcg_gen_andi_tl(R1, R1, 0xff); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_andi_tl(cpu_Zf, R, 0x0000ffff); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication + * and shifts the result one bit left. + */ +static int translate_FMULS(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_MUL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[16 + FMULS_Rd(opcode)]; + TCGv Rr =3D cpu_r[16 + FMULS_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */ + tcg_gen_mul_tl(R, t0, t1); /* R =3D Rd *Rr */ + tcg_gen_shli_tl(R, R, 1); + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + tcg_gen_andi_tl(R1, R1, 0xff); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_andi_tl(cpu_Zf, R, 0x0000ffff); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication + * and shifts the result one bit left. + */ +static int translate_FMULSU(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_MUL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[16 + FMULSU_Rd(opcode)]; + TCGv Rr =3D cpu_r[16 + FMULSU_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_mul_tl(R, t0, Rr); /* R =3D Rd *Rr */ + tcg_gen_shli_tl(R, R, 1); + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + tcg_gen_andi_tl(R1, R1, 0xff); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_andi_tl(cpu_Zf, R, 0x0000ffff); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Calls to a subroutine within the entire 4M (words) Program memory. The + * return address (to the instruction after the CALL) will be stored onto= the + * Stack. See also RCALL. The Stack Pointer uses a post-decrement scheme = during + * CALL. This instruction is not available in all devices. Refer to the = device + * specific instruction set summary. + */ +static int translate_ICALL(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_IJMP_ICALL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + int ret =3D ctx->inst[0].npc; + + gen_push_ret(ctx, ret); + gen_jmp_z(); + + return BS_BRANCH; +} + +/* + * Indirect jump to the address pointed to by the Z (16 bits) Pointer + * Register in the Register File. The Z-pointer Register is 16 bits wide = and + * allows jump within the lowest 64K words (128KB) section of Program mem= ory. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static int translate_IJMP(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_IJMP_ICALL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + gen_jmp_z(); + + return BS_BRANCH; +} + +/* + * Loads data from the I/O Space (Ports, Timers, Configuration Registers, + * etc.) into register Rd in the Register File. + */ +static int translate_IN(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[IN_Rd(opcode)]; + int Imm =3D IN_Imm(opcode); + TCGv port =3D tcg_const_i32(Imm); + + gen_helper_inb(Rd, cpu_env, port); + + tcg_temp_free_i32(port); + + return BS_NONE; +} + +/* + * Adds one -1- to the contents of register Rd and places the result in t= he + * destination register Rd. The C Flag in SREG is not affected by the + * operation, thus allowing the INC instruction to be used on a loop coun= ter in + * multiple-precision computations. When operating on unsigned numbers, = only + * BREQ and BRNE branches can be expected to perform consistently. When + * operating on two's complement values, all signed branches are availabl= e. + */ +static int translate_INC(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[INC_Rd(opcode)]; + + tcg_gen_addi_tl(Rd, Rd, 1); + tcg_gen_andi_tl(Rd, Rd, 0xff); + + /* cpu_Vf =3D Rd =3D=3D 0x80 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x80); + gen_ZNSf(Rd); + return BS_NONE; +} + +/* + * Jump to an address within the entire 4M (words) Program memory. See al= so + * RJMP. This instruction is not available in all devices. Refer to the = device + * specific instruction set summary.0 + */ +static int translate_JMP(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_JMP_CALL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + gen_goto_tb(ctx, 0, JMP_Imm(opcode)); + return BS_BRANCH; +} + +/* + * Load one byte indirect from data space to register and stores an clear + * the bits in data space specified by the register. The instruction can = only + * be used towards internal SRAM. The data location is pointed to by the= Z (16 + * bits) Pointer Register in the Register File. Memory access is limited = to the + * current data segment of 64KB. To access another data segment in device= s with + * more than 64KB data space, the RAMPZ in register in the I/O area has t= o be + * changed. The Z-pointer Register is left unchanged by the operation. T= his + * instruction is especially suited for clearing status bits stored in SR= AM. + */ +static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) +{ + if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { + gen_helper_fullwr(cpu_env, data, addr); + } else { + tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] =3D data = */ + } +} + +static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr) +{ + if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { + gen_helper_fullrd(data, cpu_env, addr); + } else { + tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data =3D mem[addr]= */ + } +} + +static int translate_LAC(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_RMW) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rr =3D cpu_r[LAC_Rr(opcode)]; + TCGv addr =3D gen_get_zaddr(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + gen_data_load(ctx, t0, addr); /* t0 =3D mem[addr] */ + /* t1 =3D t0 & (0xff - Rr) =3D t0 and ~Rr */ + tcg_gen_andc_tl(t1, t0, Rr); + + tcg_gen_mov_tl(Rr, t0); /* Rr =3D t0 */ + gen_data_store(ctx, t1, addr); /* mem[addr] =3D t1 */ + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Load one byte indirect from data space to register and set bits in data + * space specified by the register. The instruction can only be used towa= rds + * internal SRAM. The data location is pointed to by the Z (16 bits) Poi= nter + * Register in the Register File. Memory access is limited to the current= data + * segment of 64KB. To access another data segment in devices with more t= han + * 64KB data space, the RAMPZ in register in the I/O area has to be chang= ed. + * The Z-pointer Register is left unchanged by the operation. This instru= ction + * is especially suited for setting status bits stored in SRAM. + */ +static int translate_LAS(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_RMW) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rr =3D cpu_r[LAS_Rr(opcode)]; + TCGv addr =3D gen_get_zaddr(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + gen_data_load(ctx, t0, addr); /* t0 =3D mem[addr] */ + tcg_gen_or_tl(t1, t0, Rr); + + tcg_gen_mov_tl(Rr, t0); /* Rr =3D t0 */ + gen_data_store(ctx, t1, addr); /* mem[addr] =3D t1 */ + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Load one byte indirect from data space to register and toggles bits in + * the data space specified by the register. The instruction can only be= used + * towards SRAM. The data location is pointed to by the Z (16 bits) Poin= ter + * Register in the Register File. Memory access is limited to the current= data + * segment of 64KB. To access another data segment in devices with more t= han + * 64KB data space, the RAMPZ in register in the I/O area has to be chang= ed. + * The Z-pointer Register is left unchanged by the operation. This instru= ction + * is especially suited for changing status bits stored in SRAM. + */ +static int translate_LAT(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_RMW) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rr =3D cpu_r[LAT_Rr(opcode)]; + TCGv addr =3D gen_get_zaddr(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + gen_data_load(ctx, t0, addr); /* t0 =3D mem[addr] */ + tcg_gen_xor_tl(t1, t0, Rr); + + tcg_gen_mov_tl(Rr, t0); /* Rr =3D t0 */ + gen_data_store(ctx, t1, addr); /* mem[addr] =3D t1 */ + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Loads one byte indirect from the data space to a register. For parts + * with SRAM, the data space consists of the Register File, I/O memory and + * internal SRAM (and external SRAM if applicable). For parts without SRA= M, the + * data space consists of the Register File only. In some parts the Flash + * Memory has been mapped to the data space and can be read using this co= mmand. + * The EEPROM has a separate address space. The data location is pointed= to by + * the X (16 bits) Pointer Register in the Register File. Memory access is + * limited to the current data segment of 64KB. To access another data se= gment + * in devices with more than 64KB data space, the RAMPX in register in th= e I/O + * area has to be changed. The X-pointer Register can either be left unc= hanged + * by the operation, or it can be post-incremented or predecremented. Th= ese + * features are especially suited for accessing arrays, tables, and Stack + * Pointer usage of the X-pointer Register. Note that only the low byte o= f the + * X-pointer is updated in devices with no more than 256 bytes data space= . For + * such devices, the high byte of the pointer is not used by this instruc= tion + * and can be used for other purposes. The RAMPX Register in the I/O area= is + * updated in parts with more than 64KB data space or more than 64KB Prog= ram + * memory, and the increment/decrement is added to the entire 24-bit addr= ess on + * such devices. Not all variants of this instruction is available in all + * devices. Refer to the device specific instruction set summary. In the + * Reduced Core tinyAVR the LD instruction can be used to achieve the same + * operation as LPM since the program memory is mapped to the data memory + * space. + */ +static int translate_LDX1(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDX1_Rd(opcode)]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LDX2(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDX2_Rd(opcode)]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_load(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LDX3(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDX3_Rd(opcode)]; + TCGv addr =3D gen_get_xaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_load(ctx, Rd, addr); + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Loads one byte indirect with or without displacement from the data spa= ce + * to a register. For parts with SRAM, the data space consists of the Reg= ister + * File, I/O memory and internal SRAM (and external SRAM if applicable). = For + * parts without SRAM, the data space consists of the Register File only.= In + * some parts the Flash Memory has been mapped to the data space and can = be + * read using this command. The EEPROM has a separate address space. The= data + * location is pointed to by the Y (16 bits) Pointer Register in the Regi= ster + * File. Memory access is limited to the current data segment of 64KB. To + * access another data segment in devices with more than 64KB data space,= the + * RAMPY in register in the I/O area has to be changed. The Y-pointer Re= gister + * can either be left unchanged by the operation, or it can be post-incre= mented + * or predecremented. These features are especially suited for accessing + * arrays, tables, and Stack Pointer usage of the Y-pointer Register. Not= e that + * only the low byte of the Y-pointer is updated in devices with no more = than + * 256 bytes data space. For such devices, the high byte of the pointer i= s not + * used by this instruction and can be used for other purposes. The RAMPY + * Register in the I/O area is updated in parts with more than 64KB data = space + * or more than 64KB Program memory, and the increment/decrement/displace= ment + * is added to the entire 24-bit address on such devices. Not all varian= ts of + * this instruction is available in all devices. Refer to the device spec= ific + * instruction set summary. In the Reduced Core tinyAVR the LD instructi= on can + * be used to achieve the same operation as LPM since the program memory = is + * mapped to the data memory space. + */ +static int translate_LDY2(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDY2_Rd(opcode)]; + TCGv addr =3D gen_get_yaddr(); + + gen_data_load(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LDY3(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDY3_Rd(opcode)]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_load(ctx, Rd, addr); + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LDDY(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDDY_Rd(opcode)]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_addi_tl(addr, addr, LDDY_Imm(opcode)); /* addr =3D addr + q */ + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Loads one byte indirect with or without displacement from the data spa= ce + * to a register. For parts with SRAM, the data space consists of the Reg= ister + * File, I/O memory and internal SRAM (and external SRAM if applicable). = For + * parts without SRAM, the data space consists of the Register File only.= In + * some parts the Flash Memory has been mapped to the data space and can = be + * read using this command. The EEPROM has a separate address space. The= data + * location is pointed to by the Z (16 bits) Pointer Register in the Regi= ster + * File. Memory access is limited to the current data segment of 64KB. To + * access another data segment in devices with more than 64KB data space,= the + * RAMPZ in register in the I/O area has to be changed. The Z-pointer Re= gister + * can either be left unchanged by the operation, or it can be post-incre= mented + * or predecremented. These features are especially suited for Stack Poi= nter + * usage of the Z-pointer Register, however because the Z-pointer Registe= r can + * be used for indirect subroutine calls, indirect jumps and table lookup= , it + * is often more convenient to use the X or Y-pointer as a dedicated Stack + * Pointer. Note that only the low byte of the Z-pointer is updated in de= vices + * with no more than 256 bytes data space. For such devices, the high byt= e of + * the pointer is not used by this instruction and can be used for other + * purposes. The RAMPZ Register in the I/O area is updated in parts with = more + * than 64KB data space or more than 64KB Program memory, and the + * increment/decrement/displacement is added to the entire 24-bit address= on + * such devices. Not all variants of this instruction is available in all + * devices. Refer to the device specific instruction set summary. In the + * Reduced Core tinyAVR the LD instruction can be used to achieve the same + * operation as LPM since the program memory is mapped to the data memory + * space. For using the Z-pointer for table lookup in Program memory see= the + * LPM and ELPM instructions. + */ +static int translate_LDZ2(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDZ2_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + gen_data_load(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LDZ3(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDZ3_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_load(ctx, Rd, addr); + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LDDZ(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDDZ_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_addi_tl(addr, addr, LDDZ_Imm(opcode)); + /* addr =3D addr + q */ + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Loads an 8 bit constant directly to register 16 to 31. + */ +static int translate_LDI(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[16 + LDI_Rd(opcode)]; + int imm =3D LDI_Imm(opcode); + + tcg_gen_movi_tl(Rd, imm); + + return BS_NONE; +} + +/* + * Loads one byte from the data space to a register. For parts with SRAM, + * the data space consists of the Register File, I/O memory and internal = SRAM + * (and external SRAM if applicable). For parts without SRAM, the data sp= ace + * consists of the register file only. The EEPROM has a separate address = space. + * A 16-bit address must be supplied. Memory access is limited to the cur= rent + * data segment of 64KB. The LDS instruction uses the RAMPD Register to a= ccess + * memory above 64KB. To access another data segment in devices with more= than + * 64KB data space, the RAMPD in register in the I/O area has to be chang= ed. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static int translate_LDS(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LDS_Rd(opcode)]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_rampD; + + tcg_gen_mov_tl(addr, H); /* addr =3D H:M:L */ + tcg_gen_shli_tl(addr, addr, 16); + tcg_gen_ori_tl(addr, addr, LDS_Imm(opcode)); + + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Loads one byte pointed to by the Z-register into the destination + * register Rd. This instruction features a 100% space effective constant + * initialization or constant data fetch. The Program memory is organized= in + * 16-bit words while the Z-pointer is a byte address. Thus, the least + * significant bit of the Z-pointer selects either low byte (ZLSB =3D 0) = or high + * byte (ZLSB =3D 1). This instruction can address the first 64KB (32K wo= rds) of + * Program memory. The Zpointer Register can either be left unchanged by = the + * operation, or it can be incremented. The incrementation does not apply= to + * the RAMPZ Register. Devices with Self-Programming capability can use = the + * LPM instruction to read the Fuse and Lock bit values. Refer to the de= vice + * documentation for a detailed description. The LPM instruction is not + * available in all devices. Refer to the device specific instruction set + * summary + */ +static int translate_LPM1(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_LPM) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rd =3D cpu_r[0]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_r[31]; + TCGv L =3D cpu_r[30]; + + tcg_gen_shli_tl(addr, H, 8); /* addr =3D H:L */ + tcg_gen_or_tl(addr, addr, L); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LPM2(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_LPM) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rd =3D cpu_r[LPM2_Rd(opcode)]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_r[31]; + TCGv L =3D cpu_r[30]; + + tcg_gen_shli_tl(addr, H, 8); /* addr =3D H:L */ + tcg_gen_or_tl(addr, addr, L); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_LPMX(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_LPMX) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rd =3D cpu_r[LPMX_Rd(opcode)]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_r[31]; + TCGv L =3D cpu_r[30]; + + tcg_gen_shli_tl(addr, H, 8); /* addr =3D H:L */ + tcg_gen_or_tl(addr, addr, L); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + tcg_gen_andi_tl(L, addr, 0xff); + + tcg_gen_shri_tl(addr, addr, 8); + tcg_gen_andi_tl(H, addr, 0xff); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Shifts all bits in Rd one place to the right. Bit 7 is cleared. Bit 0 = is + * loaded into the C Flag of the SREG. This operation effectively divides= an + * unsigned value by two. The C Flag can be used to round the result. + */ +static int translate_LSR(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[LSR_Rd(opcode)]; + + tcg_gen_andi_tl(cpu_Cf, Rd, 1); + + tcg_gen_shri_tl(Rd, Rd, 1); + + tcg_gen_mov_tl(cpu_Zf, Rd); + tcg_gen_movi_tl(cpu_Nf, 0); + tcg_gen_mov_tl(cpu_Vf, cpu_Cf); + tcg_gen_mov_tl(cpu_Sf, cpu_Vf); + + return BS_NONE; +} + +/* + * This instruction makes a copy of one register into another. The source + * register Rr is left unchanged, while the destination register Rd is lo= aded + * with a copy of Rr. + */ +static int translate_MOV(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[MOV_Rd(opcode)]; + TCGv Rr =3D cpu_r[MOV_Rr(opcode)]; + + tcg_gen_mov_tl(Rd, Rr); + + return BS_NONE; +} + +/* + * This instruction makes a copy of one register pair into another regist= er + * pair. The source register pair Rr+1:Rr is left unchanged, while the + * destination register pair Rd+1:Rd is loaded with a copy of Rr + 1:Rr. = This + * instruction is not available in all devices. Refer to the device speci= fic + * instruction set summary. + */ +static int translate_MOVW(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_MOVW) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv RdL =3D cpu_r[MOVW_Rd(opcode) * 2 + 0]; + TCGv RdH =3D cpu_r[MOVW_Rd(opcode) * 2 + 1]; + TCGv RrL =3D cpu_r[MOVW_Rr(opcode) * 2 + 0]; + TCGv RrH =3D cpu_r[MOVW_Rr(opcode) * 2 + 1]; + + tcg_gen_mov_tl(RdH, RrH); + tcg_gen_mov_tl(RdL, RrL); + + return BS_NONE; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit unsigned multiplicat= ion. + */ +static int translate_MUL(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_MUL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[MUL_Rd(opcode)]; + TCGv Rr =3D cpu_r[MUL_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_mul_tl(R, Rd, Rr); /* R =3D Rd *Rr */ + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + + tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf =3D R(16) */ + tcg_gen_mov_tl(cpu_Zf, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplicatio= n. + */ +static int translate_MULS(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_MUL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[16 + MULS_Rd(opcode)]; + TCGv Rr =3D cpu_r[16 + MULS_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */ + tcg_gen_mul_tl(R, t0, t1); /* R =3D Rd * Rr */ + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + + tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf =3D R(16) */ + tcg_gen_mov_tl(cpu_Zf, R); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit multiplication of a + * signed and an unsigned number. + */ +static int translate_MULSU(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_MUL) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[16 + MULSU_Rd(opcode)]; + TCGv Rr =3D cpu_r[16 + MULSU_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_mul_tl(R, t0, Rr); /* R =3D Rd *Rr */ + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_mov_tl(cpu_Zf, R); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Replaces the contents of register Rd with its two's complement; the + * value $80 is left unchanged. + */ +static int translate_NEG(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[SUB_Rd(opcode)]; + TCGv t0 =3D tcg_const_i32(0); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, t0, Rd); /* R =3D 0 - Rd */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, t0, Rd); + gen_sub_Vf(R, t0, Rd); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction performs a single cycle No Operation. + */ +static int translate_NOP(DisasContext *ctx, uint32_t opcode) +{ + + /* NOP */ + + return BS_NONE; +} + +/* + * Performs the logical OR between the contents of register Rd and regist= er + * Rr and places the result in the destination register Rd. + */ +static int translate_OR(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[OR_Rd(opcode)]; + TCGv Rr =3D cpu_r[OR_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_or_tl(R, Rd, Rr); + + tcg_gen_movi_tl(cpu_Vf, 0); + gen_ZNSf(R); + + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Performs the logical OR between the contents of register Rd and a + * constant and places the result in the destination register Rd. + */ +static int translate_ORI(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[16 + ORI_Rd(opcode)]; + int Imm =3D (ORI_Imm(opcode)); + + tcg_gen_ori_tl(Rd, Rd, Imm); /* Rd =3D Rd | Imm */ + + tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf =3D 0 */ + gen_ZNSf(Rd); + + return BS_NONE; +} + +/* + * Stores data from register Rr in the Register File to I/O Space (Ports, + * Timers, Configuration Registers, etc.). + */ +static int translate_OUT(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[OUT_Rd(opcode)]; + int Imm =3D OUT_Imm(opcode); + TCGv port =3D tcg_const_i32(Imm); + + gen_helper_outb(cpu_env, port, Rd); + + tcg_temp_free_i32(port); + + return BS_NONE; +} + +/* + * This instruction loads register Rd with a byte from the STACK. The Sta= ck + * Pointer is pre-incremented by 1 before the POP. This instruction is n= ot + * available in all devices. Refer to the device specific instruction set + * summary. + */ +static int translate_POP(DisasContext *ctx, uint32_t opcode) +{ + /* + * Using a temp to work around some strange behaviour: + * tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + * gen_data_load(ctx, Rd, cpu_sp); + * seems to cause the add to happen twice. + * This doesn't happen if either the add or the load is removed. + */ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv Rd =3D cpu_r[POP_Rd(opcode)]; + + tcg_gen_addi_tl(t1, cpu_sp, 1); + gen_data_load(ctx, Rd, t1); + tcg_gen_mov_tl(cpu_sp, t1); + + return BS_NONE; +} + +/* + * This instruction stores the contents of register Rr on the STACK. The + * Stack Pointer is post-decremented by 1 after the PUSH. This instructi= on is + * not available in all devices. Refer to the device specific instruction= set + * summary. + */ +static int translate_PUSH(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[PUSH_Rd(opcode)]; + + gen_data_store(ctx, Rd, cpu_sp); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + return BS_NONE; +} + +/* + * Relative call to an address within PC - 2K + 1 and PC + 2K (words). The + * return address (the instruction after the RCALL) is stored onto the St= ack. + * See also CALL. For AVR microcontrollers with Program memory not exceed= ing 4K + * words (8KB) this instruction can address the entire memory from every + * address location. The Stack Pointer uses a post-decrement scheme during + * RCALL. + */ +static int translate_RCALL(DisasContext *ctx, uint32_t opcode) +{ + int ret =3D ctx->inst[0].npc; + int dst =3D ctx->inst[0].npc + sextract32(RCALL_Imm(opcode), 0, 12); + + gen_push_ret(ctx, ret); + gen_goto_tb(ctx, 0, dst); + + return BS_BRANCH; +} + +/* + * Returns from subroutine. The return address is loaded from the STACK. + * The Stack Pointer uses a preincrement scheme during RET. + */ +static int translate_RET(DisasContext *ctx, uint32_t opcode) +{ + gen_pop_ret(ctx, cpu_pc); + + tcg_gen_exit_tb(NULL, 0); + + return BS_BRANCH; +} + +/* + * Returns from interrupt. The return address is loaded from the STACK and + * the Global Interrupt Flag is set. Note that the Status Register is not + * automatically stored when entering an interrupt routine, and it is not + * restored when returning from an interrupt routine. This must be handle= d by + * the application program. The Stack Pointer uses a pre-increment scheme + * during RETI. + */ +static int translate_RETI(DisasContext *ctx, uint32_t opcode) +{ + gen_pop_ret(ctx, cpu_pc); + + tcg_gen_movi_tl(cpu_If, 1); + + tcg_gen_exit_tb(NULL, 0); + + return BS_BRANCH; +} + +/* + * Relative jump to an address within PC - 2K +1 and PC + 2K (words). For + * AVR microcontrollers with Program memory not exceeding 4K words (8KB) = this + * instruction can address the entire memory from every address location.= See + * also JMP. + */ +static int translate_RJMP(DisasContext *ctx, uint32_t opcode) +{ + int dst =3D ctx->inst[0].npc + sextract32(RJMP_Imm(opcode), 0, 12); + + gen_goto_tb(ctx, 0, dst); + + return BS_BRANCH; +} + +/* + * Shifts all bits in Rd one place to the right. The C Flag is shifted in= to + * bit 7 of Rd. Bit 0 is shifted into the C Flag. This operation, combin= ed + * with ASR, effectively divides multi-byte signed values by two. Combine= d with + * LSR it effectively divides multi-byte unsigned values by two. The Carr= y Flag + * can be used to round the result. + */ +static int translate_ROR(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[ROR_Rd(opcode)]; + TCGv t0 =3D tcg_temp_new_i32(); + + tcg_gen_shli_tl(t0, cpu_Cf, 7); + tcg_gen_andi_tl(cpu_Cf, Rd, 1); + tcg_gen_shri_tl(Rd, Rd, 1); + tcg_gen_or_tl(Rd, Rd, t0); + + gen_rshift_ZNVSf(Rd); + + tcg_temp_free_i32(t0); + + return BS_NONE; +} + +/* + * Subtracts two registers and subtracts with the C Flag and places the + * result in the destination register Rd. + */ +static int translate_SBC(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[SBC_Rd(opcode)]; + TCGv Rr =3D cpu_r[SBC_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr - Cf */ + tcg_gen_sub_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_NSf(R); + + /* + * Previous value remains unchanged when the result is zero; + * cleared otherwise. + */ + tcg_gen_or_tl(cpu_Zf, cpu_Zf, R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * SBCI -- Subtract Immediate with Carry + */ +static int translate_SBCI(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[16 + SBCI_Rd(opcode)]; + TCGv Rr =3D tcg_const_i32(SBCI_Imm(opcode)); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr - Cf */ + tcg_gen_sub_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_NSf(R); + + /* + * Previous value remains unchanged when the result is zero; + * cleared otherwise. + */ + tcg_gen_or_tl(cpu_Zf, cpu_Zf, R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + tcg_temp_free_i32(Rr); + + return BS_NONE; +} + +/* + * Sets a specified bit in an I/O Register. This instruction operates on + * the lower 32 I/O Registers -- addresses 0-31. + */ +static int translate_SBI(DisasContext *ctx, uint32_t opcode) +{ + TCGv data =3D tcg_temp_new_i32(); + TCGv port =3D tcg_const_i32(SBI_Imm(opcode)); + + gen_helper_inb(data, cpu_env, port); + tcg_gen_ori_tl(data, data, 1 << SBI_Bit(opcode)); + gen_helper_outb(cpu_env, port, data); + + tcg_temp_free_i32(port); + tcg_temp_free_i32(data); + + return BS_NONE; +} + +/* + * This instruction tests a single bit in an I/O Register and skips the + * next instruction if the bit is cleared. This instruction operates on t= he + * lower 32 I/O Registers -- addresses 0-31. + */ +static int translate_SBIC(DisasContext *ctx, uint32_t opcode) +{ + TCGv data =3D tcg_temp_new_i32(); + TCGv port =3D tcg_const_i32(SBIC_Imm(opcode)); + TCGLabel *skip =3D gen_new_label(); + + gen_helper_inb(data, cpu_env, port); + + /* PC if next inst is skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[1].npc); + tcg_gen_andi_tl(data, data, 1 << SBIC_Bit(opcode)); + tcg_gen_brcondi_i32(TCG_COND_EQ, data, 0, skip); + /* PC if next inst is not skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[0].npc); + gen_set_label(skip); + + tcg_temp_free_i32(port); + tcg_temp_free_i32(data); + + return BS_BRANCH; +} + +/* + * This instruction tests a single bit in an I/O Register and skips the + * next instruction if the bit is set. This instruction operates on the l= ower + * 32 I/O Registers -- addresses 0-31. + */ +static int translate_SBIS(DisasContext *ctx, uint32_t opcode) +{ + TCGv data =3D tcg_temp_new_i32(); + TCGv port =3D tcg_const_i32(SBIS_Imm(opcode)); + TCGLabel *skip =3D gen_new_label(); + + gen_helper_inb(data, cpu_env, port); + + /* PC if next inst is skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[1].npc); + tcg_gen_andi_tl(data, data, 1 << SBIS_Bit(opcode)); + tcg_gen_brcondi_i32(TCG_COND_NE, data, 0, skip); + /* PC if next inst is not skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[0].npc); + gen_set_label(skip); + + tcg_temp_free_i32(port); + tcg_temp_free_i32(data); + + return BS_BRANCH; +} + +/* + * Subtracts an immediate value (0-63) from a register pair and places the + * result in the register pair. This instruction operates on the upper fo= ur + * register pairs, and is well suited for operations on the Pointer Regis= ters. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static int translate_SBIW(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_ADIW_SBIW) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv RdL =3D cpu_r[24 + 2 * SBIW_Rd(opcode)]; + TCGv RdH =3D cpu_r[25 + 2 * SBIW_Rd(opcode)]; + int Imm =3D (SBIW_Imm(opcode)); + TCGv R =3D tcg_temp_new_i32(); + TCGv Rd =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd =3D RdH:RdL */ + tcg_gen_subi_tl(R, Rd, Imm); /* R =3D Rd - Imm */ + tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */ + + /* Cf */ + tcg_gen_andc_tl(cpu_Cf, R, Rd); + tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15); /* Cf =3D R & ~Rd */ + + /* Vf */ + tcg_gen_andc_tl(cpu_Vf, Rd, R); + tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15); /* Vf =3D Rd & ~R */ + + /* Zf */ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + + /* Nf */ + tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf =3D R(15) */ + + /* Sf */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ + + /* R */ + tcg_gen_andi_tl(RdL, R, 0xff); + tcg_gen_shri_tl(RdH, R, 8); + + tcg_temp_free_i32(Rd); + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * This instruction tests a single bit in a register and skips the next + * instruction if the bit is cleared. + */ +static int translate_SBRC(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rr =3D cpu_r[SBRC_Rr(opcode)]; + TCGv t0 =3D tcg_temp_new_i32(); + TCGLabel *skip =3D gen_new_label(); + + /* PC if next inst is skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[1].npc); + tcg_gen_andi_tl(t0, Rr, 1 << SBRC_Bit(opcode)); + tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, skip); + /* PC if next inst is not skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[0].npc); + gen_set_label(skip); + + tcg_temp_free_i32(t0); + + return BS_BRANCH; +} + +/* + * This instruction tests a single bit in a register and skips the next + * instruction if the bit is set. + */ +static int translate_SBRS(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rr =3D cpu_r[SBRS_Rr(opcode)]; + TCGv t0 =3D tcg_temp_new_i32(); + TCGLabel *skip =3D gen_new_label(); + + /* PC if next inst is skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[1].npc); + tcg_gen_andi_tl(t0, Rr, 1 << SBRS_Bit(opcode)); + tcg_gen_brcondi_i32(TCG_COND_NE, t0, 0, skip); + /* PC if next inst is not skipped */ + tcg_gen_movi_tl(cpu_pc, ctx->inst[0].npc); + gen_set_label(skip); + + tcg_temp_free_i32(t0); + + return BS_BRANCH; +} + +/* + * This instruction sets the circuit in sleep mode defined by the MCU + * Control Register. + */ +static int translate_SLEEP(DisasContext *ctx, uint32_t opcode) +{ + gen_helper_sleep(cpu_env); + + return BS_EXCP; +} + +/* + * SPM can be used to erase a page in the Program memory, to write a page + * in the Program memory (that is already erased), and to set Boot Loader= Lock + * bits. In some devices, the Program memory can be written one word at a= time, + * in other devices an entire page can be programmed simultaneously after= first + * filling a temporary page buffer. In all cases, the Program memory must= be + * erased one page at a time. When erasing the Program memory, the RAMPZ = and + * Z-register are used as page address. When writing the Program memory, = the + * RAMPZ and Z-register are used as page or word address, and the R1:R0 + * register pair is used as data(1). When setting the Boot Loader Lock bi= ts, + * the R1:R0 register pair is used as data. Refer to the device documenta= tion + * for detailed description of SPM usage. This instruction can address the + * entire Program memory. The SPM instruction is not available in all de= vices. + * Refer to the device specific instruction set summary. Note: 1. R1 + * determines the instruction high byte, and R0 determines the instructio= n low + * byte. + */ +static int translate_SPM(DisasContext *ctx, uint32_t opcode) +{ + /* TODO */ + if (avr_feature(ctx->env, AVR_FEATURE_SPM) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + return BS_NONE; +} + +static int translate_SPMX(DisasContext *ctx, uint32_t opcode) +{ + /* TODO */ + if (avr_feature(ctx->env, AVR_FEATURE_SPMX) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + return BS_NONE; +} + +static int translate_STX1(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STX1_Rr(opcode)]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STX2(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STX2_Rr(opcode)]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_store(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STX3(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STX3_Rr(opcode)]; + TCGv addr =3D gen_get_xaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_store(ctx, Rd, addr); + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STY2(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STY2_Rd(opcode)]; + TCGv addr =3D gen_get_yaddr(); + + gen_data_store(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STY3(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STY3_Rd(opcode)]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_store(ctx, Rd, addr); + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STDY(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STDY_Rd(opcode)]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_addi_tl(addr, addr, STDY_Imm(opcode)); + /* addr =3D addr + q */ + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STZ2(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STZ2_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + gen_data_store(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STZ3(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STZ3_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_store(ctx, Rd, addr); + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +static int translate_STDZ(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STDZ_Rd(opcode)]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_addi_tl(addr, addr, STDZ_Imm(opcode)); + /* addr =3D addr + q */ + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Stores one byte from a Register to the data space. For parts with SRAM, + * the data space consists of the Register File, I/O memory and internal = SRAM + * (and external SRAM if applicable). For parts without SRAM, the data sp= ace + * consists of the Register File only. The EEPROM has a separate address = space. + * A 16-bit address must be supplied. Memory access is limited to the cur= rent + * data segment of 64KB. The STS instruction uses the RAMPD Register to a= ccess + * memory above 64KB. To access another data segment in devices with more= than + * 64KB data space, the RAMPD in register in the I/O area has to be chang= ed. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static int translate_STS(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[STS_Rd(opcode)]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_rampD; + + tcg_gen_mov_tl(addr, H); /* addr =3D H:M:L */ + tcg_gen_shli_tl(addr, addr, 16); + tcg_gen_ori_tl(addr, addr, STS_Imm(opcode)); + + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +/* + * Subtracts two registers and places the result in the destination + * register Rd. + */ +static int translate_SUB(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[SUB_Rd(opcode)]; + TCGv Rr =3D cpu_r[SUB_Rr(opcode)]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return BS_NONE; +} + +/* + * Subtracts a register and a constant and places the result in the + * destination register Rd. This instruction is working on Register R16 t= o R31 + * and is very well suited for operations on the X, Y, and Z-pointers. + */ +static int translate_SUBI(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[16 + SUBI_Rd(opcode)]; + TCGv Rr =3D tcg_const_i32(SUBI_Imm(opcode)); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); + /* R =3D Rd - Imm */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + tcg_temp_free_i32(Rr); + + return BS_NONE; +} + +/* + * Swaps high and low nibbles in a register. + */ +static int translate_SWAP(DisasContext *ctx, uint32_t opcode) +{ + TCGv Rd =3D cpu_r[SWAP_Rd(opcode)]; + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_andi_tl(t0, Rd, 0x0f); + tcg_gen_shli_tl(t0, t0, 4); + tcg_gen_andi_tl(t1, Rd, 0xf0); + tcg_gen_shri_tl(t1, t1, 4); + tcg_gen_or_tl(Rd, t0, t1); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + + return BS_NONE; +} + +/* + * This instruction resets the Watchdog Timer. This instruction must be + * executed within a limited time given by the WD prescaler. See the Watc= hdog + * Timer hardware specification. + */ +static int translate_WDR(DisasContext *ctx, uint32_t opcode) +{ + gen_helper_wdr(cpu_env); + + return BS_NONE; +} + +/* + * Exchanges one byte indirect between register and data space. The data + * location is pointed to by the Z (16 bits) Pointer Register in the Regi= ster + * File. Memory access is limited to the current data segment of 64KB. To + * access another data segment in devices with more than 64KB data space,= the + * RAMPZ in register in the I/O area has to be changed. The Z-pointer Re= gister + * is left unchanged by the operation. This instruction is especially sui= ted + * for writing/reading status bits stored in SRAM. + */ +static int translate_XCH(DisasContext *ctx, uint32_t opcode) +{ + if (avr_feature(ctx->env, AVR_FEATURE_RMW) =3D=3D false) { + gen_helper_unsupported(cpu_env); + + return BS_EXCP; + } + + TCGv Rd =3D cpu_r[XCH_Rd(opcode)]; + TCGv t0 =3D tcg_temp_new_i32(); + TCGv addr =3D gen_get_zaddr(); + + gen_data_load(ctx, t0, addr); + gen_data_store(ctx, Rd, addr); + tcg_gen_mov_tl(Rd, t0); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return BS_NONE; +} + +void avr_cpu_tcg_init(void) +{ + int i; + + /* Table of instructions in human readable form */ + const Instruction instructions[] =3D { + {"ADC", "0001_11**_****_****", translate_ADC}, + {"ADD", "0000_11**_****_****", translate_ADD}, + {"ADIW", "1001_0110_****_****", translate_ADIW}, + {"AND", "0010_00**_****_****", translate_AND}, + {"ANDI", "0111_****_****_****", translate_ANDI}, + {"ASR", "1001_010*_****_0101", translate_ASR}, + {"BCLR", "1001_0100_1***_1000", translate_BCLR}, + {"BLD", "1111_100*_****_0***", translate_BLD}, + {"BRBC", "1111_01**_****_****", translate_BRBC}, + {"BRBS", "1111_00**_****_****", translate_BRBS}, + {"BREAK", "1001_0101_1001_1000", translate_BREAK}, + {"BSET", "1001_0100_0***_1000", translate_BSET}, + {"BST", "1111_101*_****_0***", translate_BST}, + {"CALL", "1001_010*_****_111*__****_****_****_****", translate_CAL= L}, + {"CBI", "1001_1000_****_****", translate_CBI}, + {"COM", "1001_010*_****_0000", translate_COM}, + {"CP", "0001_01**_****_****", translate_CP}, + {"CPC", "0000_01**_****_****", translate_CPC}, + {"CPI", "0011_****_****_****", translate_CPI}, + {"CPSE", "0001_00**_****_****", translate_CPSE}, + {"DEC", "1001_010*_****_1010", translate_DEC}, + {"DES", "1001_0100_****_1011", translate_DES}, + {"EICALL", "1001_0101_0001_1001", translate_EICALL}, + {"EIJMP", "1001_0100_0001_1001", translate_EIJMP}, + {"ELPM1", "1001_0101_1101_1000", translate_ELPM1}, + {"ELPM2", "1001_000*_****_0110", translate_ELPM2}, + {"ELPMX", "1001_000*_****_0111", translate_ELPMX}, + {"EOR", "0010_01**_****_****", translate_EOR}, + {"FMUL", "0000_0011_0***_1***", translate_FMUL}, + {"FMULS", "0000_0011_1***_0***", translate_FMULS}, + {"FMULSU", "0000_0011_1***_1***", translate_FMULSU}, + {"ICALL", "1001_0101_0000_1001", translate_ICALL}, + {"IJMP", "1001_0100_0000_1001", translate_IJMP}, + {"IN", "1011_0***_****_****", translate_IN}, + {"INC", "1001_010*_****_0011", translate_INC}, + {"JMP", "1001_010*_****_110*__****_****_****_****", translate_JMP}, + {"LAC", "1001_001*_****_0110", translate_LAC}, + {"LAS", "1001_001*_****_0101", translate_LAS}, + {"LAT", "1001_001*_****_0111", translate_LAT}, + {"LDX1", "1001_000*_****_1100", translate_LDX1}, + {"LDX2", "1001_000*_****_1101", translate_LDX2}, + {"LDX3", "1001_000*_****_1110", translate_LDX3}, + {"LDY2", "1001_000*_****_1001", translate_LDY2}, + {"LDY3", "1001_000*_****_1010", translate_LDY3}, + {"LDDY", "10*0_**0*_****_1***", translate_LDDY}, + {"LDZ2", "1001_000*_****_0001", translate_LDZ2}, + {"LDZ3", "1001_000*_****_0010", translate_LDZ3}, + {"LDDZ", "10*0_**0*_****_0***", translate_LDDZ}, + {"LDI", "1110_****_****_****", translate_LDI}, + {"LDS", "1001_000*_****_0000__****_****_****_****", translate_LDS}, + {"LPM1", "1001_0101_1100_1000", translate_LPM1}, + {"LPM2", "1001_000*_****_0100", translate_LPM2}, + {"LPMX", "1001_000*_****_0101", translate_LPMX}, + {"LSR", "1001_010*_****_0110", translate_LSR}, + {"MOV", "0010_11**_****_****", translate_MOV}, + {"MOVW", "0000_0001_****_****", translate_MOVW}, + {"MUL", "1001_11**_****_****", translate_MUL}, + {"MULS", "0000_0010_****_****", translate_MULS}, + {"MULSU", "0000_0011_0***_0***", translate_MULSU}, + {"NEG", "1001_010*_****_0001", translate_NEG}, + {"NOP", "0000_0000_0000_0000", translate_NOP}, + {"OR", "0010_10**_****_****", translate_OR}, + {"ORI", "0110_****_****_****", translate_ORI}, + {"OUT", "1011_1***_****_****", translate_OUT}, + {"POP", "1001_000*_****_1111", translate_POP}, + {"PUSH", "1001_001*_****_1111", translate_PUSH}, + {"RCALL", "1101_****_****_****", translate_RCALL}, + {"RET", "1001_0101_0000_1000", translate_RET}, + {"RETI", "1001_0101_0001_1000", translate_RETI}, + {"RJMP", "1100_****_****_****", translate_RJMP}, + {"ROR", "1001_010*_****_0111", translate_ROR}, + {"SBC", "0000_10**_****_****", translate_SBC}, + {"SBCI", "0100_****_****_****", translate_SBCI}, + {"SBI", "1001_1010_****_****", translate_SBI}, + {"SBIC", "1001_1001_****_****", translate_SBIC}, + {"SBIS", "1001_1011_****_****", translate_SBIS}, + {"SBIW", "1001_0111_****_****", translate_SBIW}, + {"SBRC", "1111_110*_****_0***", translate_SBRC}, + {"SBRS", "1111_111*_****_0***", translate_SBRS}, + {"SLEEP", "1001_0101_1000_1000", translate_SLEEP}, + {"SPM", "1001_0101_1110_1000", translate_SPM}, + {"SPMX", "1001_0101_1111_1000", translate_SPMX}, + {"STX1", "1001_001*_****_1100", translate_STX1}, + {"STX2", "1001_001*_****_1101", translate_STX2}, + {"STX3", "1001_001*_****_1110", translate_STX3}, + {"STY2", "1001_001*_****_1001", translate_STY2}, + {"STY3", "1001_001*_****_1010", translate_STY3}, + {"STDY", "10*0_**1*_****_1***", translate_STDY}, + {"STZ2", "1001_001*_****_0001", translate_STZ2}, + {"STZ3", "1001_001*_****_0010", translate_STZ3}, + {"STDZ", "10*0_**1*_****_0***", translate_STDZ}, + {"STS", "1001_001*_****_0000__****_****_****_****", translate_STS}, + {"SUB", "0001_10**_****_****", translate_SUB}, + {"SUBI", "0101_****_****_****", translate_SUBI}, + {"SWAP", "1001_010*_****_0010", translate_SWAP}, + {"WDR", "1001_0101_1010_1000", translate_WDR}, + {"XCH", "1001_001*_****_0100", translate_XCH}, + }; + avr_decoder_init(instructions, + sizeof(instructions) / sizeof(instructions[0])); + +#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x) + cpu_pc =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc"); + cpu_Cf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf"); + cpu_Zf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf"); + cpu_Nf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf"); + cpu_Vf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf"); + cpu_Sf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf"); + cpu_Hf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf"); + cpu_Tf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf"); + cpu_If =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If"); + cpu_rampD =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "ra= mpD"); + cpu_rampX =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "ra= mpX"); + cpu_rampY =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "ra= mpY"); + cpu_rampZ =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "ra= mpZ"); + cpu_eind =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind= "); + cpu_sp =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp"); + + for (i =3D 0; i < 32; i++) { + char name[16]; + + sprintf(name, "r[%d]", i); + + cpu_r[i] =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]), n= ame); + } +} + +static void decode_opc(DisasContext *ctx, InstInfo *inst) +{ + /* PC points to words. */ + inst->opcode =3D cpu_ldl_code(ctx->env, inst->cpc * 2); + inst->length =3D 0; + inst->translate =3D avr_decode(inst->opcode, &inst->length); + assert(inst->length > 0); /* Check length was set */ + + if (inst->length =3D=3D 16) { + inst->npc =3D inst->cpc + 1; + /* get opcode as 16bit value */ + inst->opcode =3D inst->opcode & 0x0000ffff; + } + if (inst->length =3D=3D 32) { + inst->npc =3D inst->cpc + 2; + /* get opcode as 32bit value */ + inst->opcode =3D (inst->opcode << 16) + | (inst->opcode >> 16); + } +} + +void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb, + int max_insns) +{ + CPUAVRState *env =3D cs->env_ptr; + DisasContext ctx =3D { + .tb =3D tb, + .env =3D env, + .memidx =3D 0, + .bstate =3D BS_NONE, + .singlestep =3D cs->singlestep_enabled, + }; + target_ulong pc_start =3D tb->pc / 2; + int num_insns =3D 0; + target_ulong cpc; + target_ulong npc; + + if (tb->flags & TB_FLAGS_FULL_ACCESS) { + /* + * This flag is set by ST/LD instruction we will regenerate it ONLY + * with mem/cpu memory access instead of mem access + */ + max_insns =3D 1; + } + + gen_tb_start(tb); + + /* decode first instruction */ + ctx.inst[0].cpc =3D pc_start; + decode_opc(&ctx, &ctx.inst[0]); + do { + /* set curr/next PCs */ + cpc =3D ctx.inst[0].cpc; + npc =3D ctx.inst[0].npc; + + /* decode next instruction */ + ctx.inst[1].cpc =3D ctx.inst[0].npc; + decode_opc(&ctx, &ctx.inst[1]); + + /* translate current instruction */ + tcg_gen_insn_start(cpc); + num_insns++; + + /* + * this is due to some strange GDB behavior + * let's assume main has address 0x100 + * b main - sets breakpoint at address 0x00000100 (code) + * b *0x100 - sets breakpoint at address 0x00800100 (data) + */ + if (unlikely(cpu_breakpoint_test(cs, OFFSET_CODE + cpc * 2, BP_ANY= )) + || cpu_breakpoint_test(cs, OFFSET_DATA + cpc * 2, BP_ANY)= ) { + tcg_gen_movi_i32(cpu_pc, cpc); + gen_helper_debug(cpu_env); + ctx.bstate =3D BS_EXCP; + goto done_generating; + } + + if (ctx.inst[0].translate) { + ctx.bstate =3D ctx.inst[0].translate(&ctx, ctx.inst[0].opcode); + } + + if (num_insns >=3D max_insns) { + break; /* max translated instructions limit reached */ + } + if (ctx.singlestep) { + break; /* single step */ + } + if ((cpc & (TARGET_PAGE_SIZE - 1)) =3D=3D 0) { + break; /* page boundary */ + } + + ctx.inst[0] =3D ctx.inst[1]; /* make next inst curr */ + } while (ctx.bstate =3D=3D BS_NONE && !tcg_op_buf_full()); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + if (ctx.singlestep) { + if (ctx.bstate =3D=3D BS_STOP || ctx.bstate =3D=3D BS_NONE) { + tcg_gen_movi_tl(cpu_pc, npc); + } + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(NULL, 0); + } else { + switch (ctx.bstate) { + case BS_STOP: + case BS_NONE: + gen_goto_tb(&ctx, 0, npc); + break; + case BS_EXCP: + case BS_BRANCH: + tcg_gen_exit_tb(NULL, 0); + break; + default: + break; + } + } + +done_generating: + gen_tb_end(tb, num_insns); + + tb->size =3D (npc - pc_start) * 2; + tb->icount =3D num_insns; +} + +void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc_w =3D data[0]; +} + +void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + int i; + + qemu_fprintf(f, "\n"); + qemu_fprintf(f, "PC: %06x\n", env->pc_w); + qemu_fprintf(f, "SP: %04x\n", env->sp); + qemu_fprintf(f, "rampD: %02x\n", env->rampD >> 16); + qemu_fprintf(f, "rampX: %02x\n", env->rampX >> 16); + qemu_fprintf(f, "rampY: %02x\n", env->rampY >> 16); + qemu_fprintf(f, "rampZ: %02x\n", env->rampZ >> 16); + qemu_fprintf(f, "EIND: %02x\n", env->eind); + qemu_fprintf(f, "X: %02x%02x\n", env->r[27], env->r[26]); + qemu_fprintf(f, "Y: %02x%02x\n", env->r[29], env->r[28]); + qemu_fprintf(f, "Z: %02x%02x\n", env->r[31], env->r[30]); + qemu_fprintf(f, "SREG: [ %c %c %c %c %c %c %c %c ]\n", + env->sregI ? 'I' : '-', + env->sregT ? 'T' : '-', + env->sregH ? 'H' : '-', + env->sregS ? 'S' : '-', + env->sregV ? 'V' : '-', + env->sregN ? '-' : 'N', /* Zf has negative logic */ + env->sregZ ? 'Z' : '-', + env->sregC ? 'I' : '-'); + + qemu_fprintf(f, "\n"); + for (i =3D 0; i < ARRAY_SIZE(env->r); i++) { + qemu_fprintf(f, "R[%02d]: %02x ", i, env->r[i]); + + if ((i % 8) =3D=3D 7) { + qemu_fprintf(f, "\n"); + } + } + qemu_fprintf(f, "\n"); +} --=20 2.21.0 From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: temperror (zoho.com: Error in retrieving data from DNS) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=temperror (zoho.com: Error in retrieving data from DNS) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976325; cv=none; d=zoho.com; s=zohoarc; b=gu1k4pxjQJ6uqNPC2oJnJhPDetrofSqVqTU3EF4driaXe/QRVZlK50AeBO+KNS9/KOFYbKQTxwWqGxA4JCZKs7zoBlB474rsxz6rVg3cvhRua2MVLMcJw/1NtOJlZEuYRxDxzmFIbC7E/kjKqvX/Gnl84zoawNCnDywznFt9vhw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976325; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=8RHkB4v0JgYljxvSG4T3yC8D4BnTouQc1twGDzWo5nA=; b=RIIN5oPdTd5QUbUQgXFfOCRvPPTsY8X9E9d9oasvYpisafvXwoP8vf3+u5ojMBpZHYWhe5hQ54L9vkC/5lhQHkJBFcY4F000I5vK+asYFcOcgm3p+u3gzNBi+xutup011yYI0kJVYNsi1uYz2FMXIxwE1XXesaT3+ZNtD27/qlI= ARC-Authentication-Results: i=1; mx.zoho.com; spf=temperror (zoho.com: Error in retrieving data from DNS) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (209.51.188.17 [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976325738385.7776093974487; Sat, 4 May 2019 06:25:25 -0700 (PDT) Received: from localhost ([127.0.0.1]:56622 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMuf8-0006LL-By for importer@patchew.org; Sat, 04 May 2019 09:25:10 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44388) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDR-0007Yd-Dp for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDN-0002wm-48 for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:17 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:42715) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002ud-NJ for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:12 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDK-000Jin-3b; Sat, 04 May 2019 09:40:10 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:36 +0100 Message-Id: <20190504083638.13380-7-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:02 -0400 Subject: [Qemu-devel] [PATCH v1 6/8] target/avr: Add limited support for USART and 16 bit timer peripherals X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" These were designed to facilitate testing but should provide enough functio= n to be useful in other contexts. Only a subset of the functions of each peripheral is implemented, mainly du= e to the lack of a standard way to handle electrical connections (like GPIO= pins). Signed-off-by: Sarah Harris --- hw/char/Kconfig | 3 + hw/char/Makefile.objs | 1 + hw/char/avr_usart.c | 316 ++++++++++++++++++ hw/timer/Kconfig | 3 + hw/timer/Makefile.objs | 1 + hw/timer/avr_timer16.c | 587 +++++++++++++++++++++++++++++++++ include/hw/char/avr_usart.h | 99 ++++++ include/hw/timer/avr_timer16.h | 99 ++++++ 8 files changed, 1109 insertions(+) create mode 100644 hw/char/avr_usart.c create mode 100644 hw/timer/avr_timer16.c create mode 100644 include/hw/char/avr_usart.h create mode 100644 include/hw/timer/avr_timer16.h diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 6360c9fffa..7af2c6dd21 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -40,3 +40,6 @@ config SCLPCONSOLE =20 config TERMINAL3270 bool + +config AVR_USART + bool diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index cf086e7114..962556d00d 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -20,6 +20,7 @@ obj-$(CONFIG_PSERIES) +=3D spapr_vty.o obj-$(CONFIG_DIGIC) +=3D digic-uart.o obj-$(CONFIG_STM32F2XX_USART) +=3D stm32f2xx_usart.o obj-$(CONFIG_RASPI) +=3D bcm2835_aux.o +obj-$(CONFIG_AVR_USART) +=3D avr_usart.o =20 common-obj-$(CONFIG_CMSDK_APB_UART) +=3D cmsdk-apb-uart.o common-obj-$(CONFIG_ETRAXFS) +=3D etraxfs_ser.o diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c new file mode 100644 index 0000000000..26c711336b --- /dev/null +++ b/hw/char/avr_usart.c @@ -0,0 +1,316 @@ +/* + * AVR USART + * + * Copyright (c) 2018 University of Kent + * Author: Sarah Harris + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/char/avr_usart.h" +#include "qemu/log.h" + +static int avr_usart_can_receive(void *opaque) +{ + AVRUsartState *usart =3D opaque; + + if (usart->data_valid || !(usart->csrb & USART_CSRB_RXEN)) { + return 0; + } + return 1; +} + +static void avr_usart_receive(void *opaque, const uint8_t *buffer, int siz= e) +{ + AVRUsartState *usart =3D opaque; + assert(size =3D=3D 1); + assert(!usart->data_valid); + usart->data =3D buffer[0]; + usart->data_valid =3D true; + usart->csra |=3D USART_CSRA_RXC; + if (usart->csrb & USART_CSRB_RXCIE) { + qemu_set_irq(usart->rxc_irq, 1); + } +} + +static void update_char_mask(AVRUsartState *usart) +{ + uint8_t mode =3D ((usart->csrc & USART_CSRC_CSZ0) ? 1 : 0) | + ((usart->csrc & USART_CSRC_CSZ1) ? 2 : 0) | + ((usart->csrb & USART_CSRB_CSZ2) ? 4 : 0); + switch (mode) { + case 0: + usart->char_mask =3D 0b11111; + break; + case 1: + usart->char_mask =3D 0b111111; + break; + case 2: + usart->char_mask =3D 0b1111111; + break; + case 3: + usart->char_mask =3D 0b11111111; + break; + case 4: + /* Fallthrough. */ + case 5: + /* Fallthrough. */ + case 6: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Reserved character size 0x%x\n", + __func__, + mode); + break; + case 7: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Nine bit character size not supported (forcing eight)\n", + __func__); + usart->char_mask =3D 0b11111111; + break; + default: + assert(0); + } +} + +static void avr_usart_reset(DeviceState *dev) +{ + AVRUsartState *usart =3D AVR_USART(dev); + usart->data_valid =3D false; + usart->csra =3D 0b00100000; + usart->csrb =3D 0b00000000; + usart->csrc =3D 0b00000110; + usart->brrl =3D 0; + usart->brrh =3D 0; + update_char_mask(usart); + qemu_set_irq(usart->rxc_irq, 0); + qemu_set_irq(usart->txc_irq, 0); + qemu_set_irq(usart->dre_irq, 0); +} + +static uint64_t avr_usart_read(void *opaque, hwaddr addr, unsigned int siz= e) +{ + AVRUsartState *usart =3D opaque; + uint8_t prr; + uint8_t data; + assert(size =3D=3D 1); + + cpu_physical_memory_read(usart->prr_address, &prr, 1); + if (prr & usart->prr_mask) { + /* USART disabled, ignore. */ + avr_usart_reset(DEVICE(usart)); + return 0; + } + + switch (addr) { + case USART_DR: + if (!(usart->csrb & USART_CSRB_RXEN)) { + /* Receiver disabled, ignore. */ + return 0; + } + if (usart->data_valid) { + data =3D usart->data & usart->char_mask; + usart->data_valid =3D false; + } else { + data =3D 0; + } + usart->csra &=3D 0xff ^ USART_CSRA_RXC; + qemu_set_irq(usart->rxc_irq, 0); + qemu_chr_fe_accept_input(&usart->chr); + return data; + case USART_CSRA: + return usart->csra; + case USART_CSRB: + return usart->csrb; + case USART_CSRC: + return usart->csrc; + case USART_BRRL: + return usart->brrl; + case USART_BRRH: + return usart->brrh; + default: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, + addr); + } + return 0; +} + +static void avr_usart_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + AVRUsartState *usart =3D opaque; + uint8_t mask; + uint8_t data; + assert((value & 0xff) =3D=3D value); + assert(size =3D=3D 1); + + uint8_t prr; + cpu_physical_memory_read(usart->prr_address, &prr, 1); + if (prr & usart->prr_mask) { + /* USART disabled, ignore. */ + avr_usart_reset(DEVICE(usart)); + return; + } + + switch (addr) { + case USART_DR: + if (!(usart->csrb & USART_CSRB_TXEN)) { + /* Transmitter disabled, ignore. */ + return; + } + usart->csra |=3D USART_CSRA_TXC; + usart->csra |=3D USART_CSRA_DRE; + if (usart->csrb & USART_CSRB_TXCIE) { + qemu_set_irq(usart->txc_irq, 1); + usart->csra &=3D 0xff ^ USART_CSRA_TXC; + } + if (usart->csrb & USART_CSRB_DREIE) { + qemu_set_irq(usart->dre_irq, 1); + } + data =3D value; + qemu_chr_fe_write_all(&usart->chr, &data, 1); + break; + case USART_CSRA: + mask =3D 0b01000011; + /* Mask read-only bits. */ + value =3D (value & mask) | (usart->csra & (0xff ^ mask)); + usart->csra =3D value; + if (value & USART_CSRA_TXC) { + usart->csra ^=3D USART_CSRA_TXC; + qemu_set_irq(usart->txc_irq, 0); + } + if (value & USART_CSRA_MPCM) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: MPCM not supported by USART\n", + __func__); + } + break; + case USART_CSRB: + mask =3D 0b11111101; + /* Mask read-only bits. */ + value =3D (value & mask) | (usart->csrb & (0xff ^ mask)); + usart->csrb =3D value; + if (!(value & USART_CSRB_RXEN)) { + /* Receiver disabled, flush input buffer. */ + usart->data_valid =3D false; + } + qemu_set_irq(usart->rxc_irq, + ((value & USART_CSRB_RXCIE) && + (usart->csra & USART_CSRA_RXC)) ? 1 : 0); + qemu_set_irq(usart->txc_irq, + ((value & USART_CSRB_TXCIE) && + (usart->csra & USART_CSRA_TXC)) ? 1 : 0); + qemu_set_irq(usart->dre_irq, + ((value & USART_CSRB_DREIE) && + (usart->csra & USART_CSRA_DRE)) ? 1 : 0); + update_char_mask(usart); + break; + case USART_CSRC: + usart->csrc =3D value; + if ((value & USART_CSRC_MSEL1) && (value & USART_CSRC_MSEL0)) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: SPI mode not supported by USART\n", + __func__); + } + if ((value & USART_CSRC_MSEL1) && !(value & USART_CSRC_MSEL0)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad USART mode\n", __func_= _); + } + if (!(value & USART_CSRC_PM1) && (value & USART_CSRC_PM0)) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Bad USART parity mode\n", + __func__); + } + update_char_mask(usart); + break; + case USART_BRRL: + usart->brrl =3D value; + break; + case USART_BRRH: + usart->brrh =3D value & 0b00001111; + break; + default: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, + addr); + } +} + +static const MemoryRegionOps avr_usart_ops =3D { + .read =3D avr_usart_read, + .write =3D avr_usart_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, +}; + +static Property avr_usart_properties[] =3D { + DEFINE_PROP_CHR("chardev", AVRUsartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void avr_usart_init(Object *obj) +{ + AVRUsartState *s =3D AVR_USART(obj); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rxc_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->dre_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->txc_irq); + memory_region_init_io(&s->mmio, obj, &avr_usart_ops, s, TYPE_AVR_USART= , 7); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void avr_usart_realize(DeviceState *dev, Error **errp) +{ + AVRUsartState *s =3D AVR_USART(dev); + qemu_chr_fe_set_handlers(&s->chr, avr_usart_can_receive, + avr_usart_receive, NULL, NULL, + s, NULL, true); + avr_usart_reset(dev); +} + +static void avr_usart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->reset =3D avr_usart_reset; + dc->props =3D avr_usart_properties; + dc->realize =3D avr_usart_realize; +} + +static const TypeInfo avr_usart_info =3D { + .name =3D TYPE_AVR_USART, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AVRUsartState), + .instance_init =3D avr_usart_init, + .class_init =3D avr_usart_class_init, +}; + +static void avr_usart_register_types(void) +{ + type_register_static(&avr_usart_info); +} + +type_init(avr_usart_register_types) diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 51921eb63f..ad754df750 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -61,3 +61,6 @@ config CMSDK_APB_TIMER config CMSDK_APB_DUALTIMER bool select PTIMER + +config AVR_TIMER16 + bool diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 0e9a4530f8..bf1ffcc52d 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -35,6 +35,7 @@ obj-$(CONFIG_PXA2XX) +=3D pxa2xx_timer.o obj-$(CONFIG_SH4) +=3D sh_timer.o obj-$(CONFIG_DIGIC) +=3D digic-timer.o obj-$(CONFIG_MIPS_CPS) +=3D mips_gictimer.o +obj-$(CONFIG_AVR_TIMER16) +=3D avr_timer16.o =20 obj-$(CONFIG_MC146818RTC) +=3D mc146818rtc.o =20 diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c new file mode 100644 index 0000000000..89f8f55eaa --- /dev/null +++ b/hw/timer/avr_timer16.c @@ -0,0 +1,587 @@ +/* + * AVR 16 bit timer + * + * Copyright (c) 2018 University of Kent + * Author: Ed Robbins + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +/* + * Driver for 16 bit timers on 8 bit AVR devices. + * Note: + * ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 bit + */ + +/* + * XXX TODO: Power Reduction Register support + * prescaler pause support + * PWM modes, GPIO, output capture pins, input compare pin + */ + +#include "qemu/osdep.h" +#include "hw/timer/avr_timer16.h" +#include "qemu/log.h" + +/* Register offsets */ +#define T16_CRA 0x0 +#define T16_CRB 0x1 +#define T16_CRC 0x2 +#define T16_CNTL 0x4 +#define T16_CNTH 0x5 +#define T16_ICRL 0x6 +#define T16_ICRH 0x7 +#define T16_OCRAL 0x8 +#define T16_OCRAH 0x9 +#define T16_OCRBL 0xa +#define T16_OCRBH 0xb +#define T16_OCRCL 0xc +#define T16_OCRCH 0xd + +/* Field masks */ +#define T16_CRA_WGM01 0x3 +#define T16_CRA_COMC 0xc +#define T16_CRA_COMB 0x30 +#define T16_CRA_COMA 0xc0 +#define T16_CRA_OC_CONF \ + (T16_CRA_COMA | T16_CRA_COMB | T16_CRA_COMC) + +#define T16_CRB_CS 0x7 +#define T16_CRB_WGM23 0x18 +#define T16_CRB_ICES 0x40 +#define T16_CRB_ICNC 0x80 + +#define T16_CRC_FOCC 0x20 +#define T16_CRC_FOCB 0x40 +#define T16_CRC_FOCA 0x80 + +/* Fields masks both TIMSK and TIFR (interrupt mask/flag registers) */ +#define T16_INT_TOV 0x1 /* Timer overflow */ +#define T16_INT_OCA 0x2 /* Output compare A */ +#define T16_INT_OCB 0x4 /* Output compare B */ +#define T16_INT_OCC 0x8 /* Output compare C */ +#define T16_INT_IC 0x20 /* Input capture */ + +/* Clock source values */ +#define T16_CLKSRC_STOPPED 0 +#define T16_CLKSRC_DIV1 1 +#define T16_CLKSRC_DIV8 2 +#define T16_CLKSRC_DIV64 3 +#define T16_CLKSRC_DIV256 4 +#define T16_CLKSRC_DIV1024 5 +#define T16_CLKSRC_EXT_FALLING 6 +#define T16_CLKSRC_EXT_RISING 7 + +/* Timer mode values (not including PWM modes) */ +#define T16_MODE_NORMAL 0 +#define T16_MODE_CTC_OCRA 4 +#define T16_MODE_CTC_ICR 12 + +/* Accessors */ +#define CLKSRC(t16) (t16->crb & T16_CRB_CS) +#define MODE(t16) (((t16->crb & T16_CRB_WGM23) >> 1) | \ + (t16->cra & T16_CRA_WGM01)) +#define CNT(t16) VAL16(t16->cntl, t16->cnth) +#define OCRA(t16) VAL16(t16->ocral, t16->ocrah) +#define OCRB(t16) VAL16(t16->ocrbl, t16->ocrbh) +#define OCRC(t16) VAL16(t16->ocrcl, t16->ocrch) +#define ICR(t16) VAL16(t16->icrl, t16->icrh) + +/* Helper macros */ +#define VAL16(l, h) ((h << 8) | l) +#define ERROR(fmt, args...) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## args) +#define DB_PRINT(fmt, args...) /* Nothing */ +/*#define DB_PRINT(fmt, args...) printf("%s: " fmt "\n", __func__, ## args= )*/ + +static inline int64_t avr_timer16_ns_to_ticks(AVRTimer16State *t16, int64_= t t) +{ + if (t16->period_ns =3D=3D 0) { + return 0; + } + return t / t16->period_ns; +} + +static void avr_timer16_update_cnt(AVRTimer16State *t16) +{ + uint16_t cnt; + cnt =3D avr_timer16_ns_to_ticks(t16, qemu_clock_get_ns(QEMU_CLOCK_VIRT= UAL) - + t16->reset_time_ns); + t16->cntl =3D (uint8_t)(cnt & 0xff); + t16->cnth =3D (uint8_t)((cnt & 0xff00) >> 8); +} + +static inline void avr_timer16_recalc_reset_time(AVRTimer16State *t16) +{ + t16->reset_time_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + CNT(t16) * t16->period_ns; +} + +static void avr_timer16_clock_reset(AVRTimer16State *t16) +{ + t16->cntl =3D 0; + t16->cnth =3D 0; + t16->reset_time_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void avr_timer16_clksrc_update(AVRTimer16State *t16) +{ + uint16_t divider =3D 0; + switch (CLKSRC(t16)) { + case T16_CLKSRC_EXT_FALLING: + case T16_CLKSRC_EXT_RISING: + ERROR("external clock source unsupported"); + goto end; + case T16_CLKSRC_STOPPED: + goto end; + case T16_CLKSRC_DIV1: + divider =3D 1; + break; + case T16_CLKSRC_DIV8: + divider =3D 8; + break; + case T16_CLKSRC_DIV64: + divider =3D 64; + break; + case T16_CLKSRC_DIV256: + divider =3D 256; + break; + case T16_CLKSRC_DIV1024: + divider =3D 1024; + break; + default: + goto end; + } + t16->freq_hz =3D t16->cpu_freq_hz / divider; + t16->period_ns =3D 1000000000ULL / t16->freq_hz; + DB_PRINT("Timer frequency %" PRIu64 " hz, period %" PRIu64 " ns (%f s)= ", + t16->freq_hz, t16->period_ns, 1 / (double)t16->freq_hz); +end: + return; +} + +static void avr_timer16_set_alarm(AVRTimer16State *t16) +{ + if (CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_FALLING || + CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_RISING || + CLKSRC(t16) =3D=3D T16_CLKSRC_STOPPED) { + /* Timer is disabled or set to external clock source (unsupported)= */ + goto end; + } + + uint64_t alarm_offset =3D 0xffff; + enum NextInterrupt next_interrupt =3D OVERFLOW; + + switch (MODE(t16)) { + case T16_MODE_NORMAL: + /* Normal mode */ + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCA)) { + alarm_offset =3D OCRA(t16); + next_interrupt =3D COMPA; + } + break; + case T16_MODE_CTC_OCRA: + /* CTC mode, top =3D ocra */ + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16)) { + alarm_offset =3D OCRA(t16); + next_interrupt =3D COMPA; + } + break; + case T16_MODE_CTC_ICR: + /* CTC mode, top =3D icr */ + if (ICR(t16) < alarm_offset && ICR(t16) > CNT(t16)) { + alarm_offset =3D ICR(t16); + next_interrupt =3D CAPT; + } + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCA)) { + alarm_offset =3D OCRA(t16); + next_interrupt =3D COMPA; + } + break; + default: + ERROR("pwm modes are unsupported"); + goto end; + } + if (OCRB(t16) < alarm_offset && OCRB(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCB)) { + alarm_offset =3D OCRB(t16); + next_interrupt =3D COMPB; + } + if (OCRC(t16) < alarm_offset && OCRB(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCC)) { + alarm_offset =3D OCRB(t16); + next_interrupt =3D COMPC; + } + alarm_offset -=3D CNT(t16); + + t16->next_interrupt =3D next_interrupt; + uint64_t alarm_ns =3D + t16->reset_time_ns + ((CNT(t16) + alarm_offset) * t16->period_ns); + timer_mod(t16->timer, alarm_ns); + + DB_PRINT("next alarm %" PRIu64 " ns from now", + alarm_offset * t16->period_ns); + +end: + return; +} + +static void avr_timer16_interrupt(void *opaque) +{ + AVRTimer16State *t16 =3D opaque; + uint8_t mode =3D MODE(t16); + + avr_timer16_update_cnt(t16); + + if (CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_FALLING || + CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_RISING || + CLKSRC(t16) =3D=3D T16_CLKSRC_STOPPED) { + /* Timer is disabled or set to external clock source (unsupported)= */ + return; + } + + DB_PRINT("interrupt, cnt =3D %d", CNT(t16)); + + /* Counter overflow */ + if (t16->next_interrupt =3D=3D OVERFLOW) { + DB_PRINT("0xffff overflow"); + avr_timer16_clock_reset(t16); + if (t16->imsk & T16_INT_TOV) { + t16->ifr |=3D T16_INT_TOV; + qemu_set_irq(t16->ovf_irq, 1); + } + } + /* Check for ocra overflow in CTC mode */ + if (mode =3D=3D T16_MODE_CTC_OCRA && t16->next_interrupt =3D=3D COMPA)= { + DB_PRINT("CTC OCRA overflow"); + avr_timer16_clock_reset(t16); + } + /* Check for icr overflow in CTC mode */ + if (mode =3D=3D T16_MODE_CTC_ICR && t16->next_interrupt =3D=3D CAPT) { + DB_PRINT("CTC ICR overflow"); + avr_timer16_clock_reset(t16); + if (t16->imsk & T16_INT_IC) { + t16->ifr |=3D T16_INT_IC; + qemu_set_irq(t16->capt_irq, 1); + } + } + /* Check for output compare interrupts */ + if (t16->imsk & T16_INT_OCA && t16->next_interrupt =3D=3D COMPA) { + t16->ifr |=3D T16_INT_OCA; + qemu_set_irq(t16->compa_irq, 1); + } + if (t16->imsk & T16_INT_OCB && t16->next_interrupt =3D=3D COMPB) { + t16->ifr |=3D T16_INT_OCB; + qemu_set_irq(t16->compb_irq, 1); + } + if (t16->imsk & T16_INT_OCC && t16->next_interrupt =3D=3D COMPC) { + t16->ifr |=3D T16_INT_OCC; + qemu_set_irq(t16->compc_irq, 1); + } + avr_timer16_set_alarm(t16); +} + +static void avr_timer16_reset(DeviceState *dev) +{ + AVRTimer16State *t16 =3D AVR_TIMER16(dev); + + avr_timer16_clock_reset(t16); + avr_timer16_clksrc_update(t16); + avr_timer16_set_alarm(t16); + + qemu_set_irq(t16->capt_irq, 0); + qemu_set_irq(t16->compa_irq, 0); + qemu_set_irq(t16->compb_irq, 0); + qemu_set_irq(t16->compc_irq, 0); + qemu_set_irq(t16->ovf_irq, 0); +} + +static uint64_t avr_timer16_read(void *opaque, hwaddr offset, unsigned siz= e) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + uint8_t retval =3D 0; + + switch (offset) { + case T16_CRA: + retval =3D t16->cra; + break; + case T16_CRB: + retval =3D t16->crb; + break; + case T16_CRC: + retval =3D t16->crc; + break; + case T16_CNTL: + avr_timer16_update_cnt(t16); + t16->rtmp =3D t16->cnth; + retval =3D t16->cntl; + break; + case T16_CNTH: + retval =3D t16->rtmp; + break; + case T16_ICRL: + /* + * The timer copies cnt to icr when the input capture pin changes + * state or when the analog comparator has a match. We don't + * emulate this behaviour. We do support it's use for defining a + * TOP value in T16_MODE_CTC_ICR + */ + t16->rtmp =3D t16->icrh; + retval =3D t16->icrl; + break; + case T16_ICRH: + retval =3D t16->rtmp; + break; + case T16_OCRAL: + retval =3D t16->ocral; + break; + case T16_OCRAH: + retval =3D t16->ocrah; + break; + case T16_OCRBL: + retval =3D t16->ocrbl; + break; + case T16_OCRBH: + retval =3D t16->ocrbh; + break; + case T16_OCRCL: + retval =3D t16->ocrcl; + break; + case T16_OCRCH: + retval =3D t16->ocrch; + break; + default: + break; + } + return (uint64_t)retval; +} + +static void avr_timer16_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + uint8_t val8 =3D (uint8_t)val64; + uint8_t prev_clk_src =3D CLKSRC(t16); + + DB_PRINT("write %d to offset %d", val8, (uint8_t)offset); + + switch (offset) { + case T16_CRA: + t16->cra =3D val8; + if (t16->cra & T16_CRA_OC_CONF) { + ERROR("output compare pins unsupported"); + } + break; + case T16_CRB: + t16->crb =3D val8; + if (t16->crb & T16_CRB_ICNC) { + ERROR("input capture noise canceller unsupported"); + } + if (t16->crb & T16_CRB_ICES) { + ERROR("input capture unsupported"); + } + if (CLKSRC(t16) !=3D prev_clk_src) { + avr_timer16_clksrc_update(t16); + if (prev_clk_src =3D=3D T16_CLKSRC_STOPPED) { + t16->reset_time_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUA= L); + } + } + break; + case T16_CRC: + t16->crc =3D val8; + ERROR("output compare pins unsupported"); + break; + case T16_CNTL: + /* + * CNT is the 16-bit counter value, it must be read/written via + * a temporary register (rtmp) to make the read/write atomic. + */ + /* ICR also has this behaviour, and shares rtmp */ + /* + * Writing CNT blocks compare matches for one clock cycle. + * Writing CNT to TOP or to an OCR value (if in use) will + * skip the relevant interrupt + */ + t16->cntl =3D val8; + t16->cnth =3D t16->rtmp; + avr_timer16_recalc_reset_time(t16); + break; + case T16_CNTH: + t16->rtmp =3D val8; + break; + case T16_ICRL: + /* ICR can only be written in mode T16_MODE_CTC_ICR */ + if (MODE(t16) =3D=3D T16_MODE_CTC_ICR) { + t16->icrl =3D val8; + t16->icrh =3D t16->rtmp; + } + break; + case T16_ICRH: + if (MODE(t16) =3D=3D T16_MODE_CTC_ICR) { + t16->rtmp =3D val8; + } + break; + case T16_OCRAL: + /* + * OCRn cause the relevant output compare flag to be raised, and + * trigger an interrupt, when CNT is equal to the value here + */ + t16->ocral =3D val8; + break; + case T16_OCRAH: + t16->ocrah =3D val8; + break; + case T16_OCRBL: + t16->ocrbl =3D val8; + break; + case T16_OCRBH: + t16->ocrbh =3D val8; + break; + case T16_OCRCL: + t16->ocrcl =3D val8; + break; + case T16_OCRCH: + t16->ocrch =3D val8; + break; + default: + break; + } + avr_timer16_set_alarm(t16); +} + +static uint64_t avr_timer16_imsk_read(void *opaque, + hwaddr offset, + unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return 0; + } + return t16->imsk; +} + +static void avr_timer16_imsk_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return; + } + t16->imsk =3D (uint8_t)val64; +} + +static uint64_t avr_timer16_ifr_read(void *opaque, + hwaddr offset, + unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return 0; + } + return t16->ifr; +} + +static void avr_timer16_ifr_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return; + } + t16->ifr =3D (uint8_t)val64; +} + +static const MemoryRegionOps avr_timer16_ops =3D { + .read =3D avr_timer16_read, + .write =3D avr_timer16_write, + .endianness =3D DEVICE_NATIVE_ENDIAN +}; + +static const MemoryRegionOps avr_timer16_imsk_ops =3D { + .read =3D avr_timer16_imsk_read, + .write =3D avr_timer16_imsk_write, + .endianness =3D DEVICE_NATIVE_ENDIAN +}; + +static const MemoryRegionOps avr_timer16_ifr_ops =3D { + .read =3D avr_timer16_ifr_read, + .write =3D avr_timer16_ifr_write, + .endianness =3D DEVICE_NATIVE_ENDIAN +}; + +static Property avr_timer16_properties[] =3D { + DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State, + cpu_freq_hz, 20000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void avr_timer16_init(Object *obj) +{ + AVRTimer16State *s =3D AVR_TIMER16(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->capt_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compa_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compb_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compc_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->ovf_irq); + + memory_region_init_io(&s->iomem, obj, &avr_timer16_ops, + s, TYPE_AVR_TIMER16, 0xe); + memory_region_init_io(&s->imsk_iomem, obj, &avr_timer16_imsk_ops, + s, TYPE_AVR_TIMER16, 0x1); + memory_region_init_io(&s->ifr_iomem, obj, &avr_timer16_ifr_ops, + s, TYPE_AVR_TIMER16, 0x1); + + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->imsk_iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->ifr_iomem); + + s->timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_timer16_interrupt, s= ); +} + +static void avr_timer16_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->reset =3D avr_timer16_reset; + dc->props =3D avr_timer16_properties; +} + +static const TypeInfo avr_timer16_info =3D { + .name =3D TYPE_AVR_TIMER16, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AVRTimer16State), + .instance_init =3D avr_timer16_init, + .class_init =3D avr_timer16_class_init, +}; + +static void avr_timer16_register_types(void) +{ + type_register_static(&avr_timer16_info); +} + +type_init(avr_timer16_register_types) diff --git a/include/hw/char/avr_usart.h b/include/hw/char/avr_usart.h new file mode 100644 index 0000000000..ba28fbe5d1 --- /dev/null +++ b/include/hw/char/avr_usart.h @@ -0,0 +1,99 @@ +/* + * AVR USART + * + * Copyright (c) 2018 University of Kent + * Author: Sarah Harris + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +#ifndef HW_AVR_USART_H +#define HW_AVR_USART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "hw/hw.h" + +/* Offsets of registers. */ +#define USART_DR 0x06 +#define USART_CSRA 0x00 +#define USART_CSRB 0x01 +#define USART_CSRC 0x02 +#define USART_BRRH 0x05 +#define USART_BRRL 0x04 + +/* Relevant bits in regiters. */ +#define USART_CSRA_RXC (1 << 7) +#define USART_CSRA_TXC (1 << 6) +#define USART_CSRA_DRE (1 << 5) +#define USART_CSRA_MPCM (1 << 0) + +#define USART_CSRB_RXCIE (1 << 7) +#define USART_CSRB_TXCIE (1 << 6) +#define USART_CSRB_DREIE (1 << 5) +#define USART_CSRB_RXEN (1 << 4) +#define USART_CSRB_TXEN (1 << 3) +#define USART_CSRB_CSZ2 (1 << 2) +#define USART_CSRB_RXB8 (1 << 1) +#define USART_CSRB_TXB8 (1 << 0) + +#define USART_CSRC_MSEL1 (1 << 7) +#define USART_CSRC_MSEL0 (1 << 6) +#define USART_CSRC_PM1 (1 << 5) +#define USART_CSRC_PM0 (1 << 4) +#define USART_CSRC_CSZ1 (1 << 2) +#define USART_CSRC_CSZ0 (1 << 1) + +#define TYPE_AVR_USART "avr-usart" +#define AVR_USART(obj) \ + OBJECT_CHECK(AVRUsartState, (obj), TYPE_AVR_USART) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + + CharBackend chr; + + /* Address of Power Reduction Register and bit that controls this UART= */ + hwaddr prr_address; + uint8_t prr_mask; + + uint8_t data; + bool data_valid; + uint8_t char_mask; + /* Control and Status Registers */ + uint8_t csra; + uint8_t csrb; + uint8_t csrc; + /* Baud Rate Registers (low/high byte) */ + uint8_t brrh; + uint8_t brrl; + + /* Receive Complete */ + qemu_irq rxc_irq; + /* Transmit Complete */ + qemu_irq txc_irq; + /* Data Register Empty */ + qemu_irq dre_irq; +} AVRUsartState; + +#endif /* HW_AVR_USART_H */ diff --git a/include/hw/timer/avr_timer16.h b/include/hw/timer/avr_timer16.h new file mode 100644 index 0000000000..301e36a154 --- /dev/null +++ b/include/hw/timer/avr_timer16.h @@ -0,0 +1,99 @@ +/* + * AVR 16 bit timer + * + * Copyright (c) 2018 University of Kent + * Author: Ed Robbins + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +/* + * Driver for 16 bit timers on 8 bit AVR devices. + * Note: + * On ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 = bit + */ + +#ifndef AVR_TIMER16_H +#define AVR_TIMER16_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/hw.h" + +enum NextInterrupt { + OVERFLOW, + COMPA, + COMPB, + COMPC, + CAPT +}; + +#define TYPE_AVR_TIMER16 "avr-timer16" +#define AVR_TIMER16(obj) \ + OBJECT_CHECK(AVRTimer16State, (obj), TYPE_AVR_TIMER16) + +typedef struct AVRTimer16State { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + MemoryRegion imsk_iomem; + MemoryRegion ifr_iomem; + QEMUTimer *timer; + qemu_irq capt_irq; + qemu_irq compa_irq; + qemu_irq compb_irq; + qemu_irq compc_irq; + qemu_irq ovf_irq; + + /* Address of Power Reduction Register and bit that controls this time= r */ + hwaddr prr_address; + uint8_t prr_mask; + + /* registers */ + uint8_t cra; + uint8_t crb; + uint8_t crc; + uint8_t cntl; + uint8_t cnth; + uint8_t icrl; + uint8_t icrh; + uint8_t ocral; + uint8_t ocrah; + uint8_t ocrbl; + uint8_t ocrbh; + uint8_t ocrcl; + uint8_t ocrch; + /* + * Reads and writes to CNT and ICR utilise a bizarre temporary + * register, which we emulate + */ + uint8_t rtmp; + uint8_t imsk; + uint8_t ifr; + + uint64_t cpu_freq_hz; + uint64_t freq_hz; + uint64_t period_ns; + uint64_t reset_time_ns; + enum NextInterrupt next_interrupt; +} AVRTimer16State; + +#endif /* AVR_TIMER16_H */ --=20 2.21.0 From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976339; cv=none; d=zoho.com; s=zohoarc; b=eWwYWiUgK/aA95TFPVBo+IpI5xKYFllpdE9NoZK4D11DKUk2XAbnQ6qP7KJ7K9Q/3v35VyGyFilVQqwuCQwy9fXUUnl+xvmMKlfWVBoadGcl2HROAGwetSwSCCzmeflVoOxE2MKqzUyZ6XJetfOuQ9WWASHY4NQhIYMUVVp2lSs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976339; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=wdV5yQBi+fk1e3UrBDLiahm41WH7Db2P1Zt8yrcARqQ=; b=I4UiAdoti+RkoL7s86YonZt51byHkszOX16HvHgdYOQj+Hzo7G4RlJw6E7sJ2NVXLImvaCBXp3O9IDayKDj6Rz+kl0sOZgn5w95qMYuoqH0tuH9SuVng4h6LVsi0LXrM/YJJoyYAgMzJuN62Akw1bIg79RJWlvcclobFmL7wkOw= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (209.51.188.17 [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976339363790.7324612733462; Sat, 4 May 2019 06:25:39 -0700 (PDT) Received: from localhost ([127.0.0.1]:56626 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMufR-0006kQ-8A for importer@patchew.org; Sat, 04 May 2019 09:25:29 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44352) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDP-0007X0-4A for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDM-0002wD-Ty for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:15 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:50456) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002uq-NC for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:12 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDK-000Jin-86; Sat, 04 May 2019 09:40:10 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:37 +0100 Message-Id: <20190504083638.13380-8-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:02 -0400 Subject: [Qemu-devel] [PATCH v1 7/8] target/avr: Add example board configuration X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" A simple board setup that configures an AVR CPU to run a given firmware ima= ge. This is all that's useful to implement without peripheral emulation as AVR = CPUs include a lot of on-board peripherals. Signed-off-by: Sarah Harris --- hw/Kconfig | 1 + hw/avr/Kconfig | 4 + hw/avr/Makefile.objs | 1 + hw/avr/sample.c | 177 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 hw/avr/Kconfig create mode 100644 hw/avr/Makefile.objs create mode 100644 hw/avr/sample.c diff --git a/hw/Kconfig b/hw/Kconfig index 88b9f15007..dc5eb7a038 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -41,6 +41,7 @@ source watchdog/Kconfig # arch Kconfig source arm/Kconfig source alpha/Kconfig +source avr/Kconfig source cris/Kconfig source hppa/Kconfig source i386/Kconfig diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig new file mode 100644 index 0000000000..c6ca8fe775 --- /dev/null +++ b/hw/avr/Kconfig @@ -0,0 +1,4 @@ +config AVR_SAMPLE + bool + select AVR_TIMER16 + select AVR_USART diff --git a/hw/avr/Makefile.objs b/hw/avr/Makefile.objs new file mode 100644 index 0000000000..626b7064b3 --- /dev/null +++ b/hw/avr/Makefile.objs @@ -0,0 +1 @@ +obj-y +=3D sample.o diff --git a/hw/avr/sample.c b/hw/avr/sample.c new file mode 100644 index 0000000000..21b384b3b3 --- /dev/null +++ b/hw/avr/sample.c @@ -0,0 +1,177 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +/* + * NOTE: + * This is not a real AVR board, this is an example! + * The CPU is an approximation of an ATmega2560, but is missing vario= us + * built-in peripherals. + * + * This example board loads provided binary file into flash memory and + * executes it from 0x00000000 address in the code memory space. + * + * Currently used for AVR CPU validation + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "ui/console.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "qemu/error-report.h" +#include "exec/address-spaces.h" +#include "include/hw/sysbus.h" +#include "include/hw/char/avr_usart.h" +#include "include/hw/timer/avr_timer16.h" +#include "elf.h" + +#define SIZE_FLASH 0x00040000 +#define SIZE_SRAM 0x00002200 +/* + * Size of additional "external" memory, as if the AVR were configured to = use + * an external RAM chip. + * Note that the configuration registers that normally enable this feature= are + * unimplemented. + */ +#define SIZE_EXMEM 0x00000000 + +/* Offsets of periphals in emulated memory space (i.e. not host addresses)= */ +#define PRR0 0x64 +#define PRR1 0x65 +#define USART_BASE 0xc0 +#define USART_PRR PRR0 +#define USART_PRR_MASK 0b00000010 +#define TIMER1_BASE 0x80 +#define TIMER1_IMSK_BASE 0x6f +#define TIMER1_IFR_BASE 0x36 +#define TIMER1_PRR PRR0 +#define TIMER1_PRR_MASK 0b01000000 + +/* Interrupt numbers used by peripherals */ +#define TIMER1_CAPT_IRQ 15 +#define TIMER1_COMPA_IRQ 16 +#define TIMER1_COMPB_IRQ 17 +#define TIMER1_COMPC_IRQ 18 +#define TIMER1_OVF_IRQ 19 + +static void sample_init(MachineState *machine) +{ + MemoryRegion *address_space_mem; + MemoryRegion *ram; + MemoryRegion *flash; + AVRCPU *cpu_avr; + const char *firmware =3D NULL; + const char *filename; + int bytes_loaded; + AVRUsartState *usart0; + AVRTimer16State *timer1; + SysBusDevice *busdev; + + address_space_mem =3D get_system_memory(); + ram =3D g_new(MemoryRegion, 1); + flash =3D g_new(MemoryRegion, 1); + + /* ATmega2560. */ + cpu_avr =3D AVR_CPU(cpu_create("avr6-avr")); + + memory_region_allocate_system_memory( + ram, NULL, "avr.ram", SIZE_SRAM + SIZE_EXMEM); + memory_region_add_subregion(address_space_mem, OFFSET_DATA, ram); + + memory_region_init_rom(flash, NULL, "avr.flash", SIZE_FLASH, &error_fa= tal); + memory_region_add_subregion(address_space_mem, OFFSET_CODE, flash); + + /* USART 0 built-in peripheral */ + usart0 =3D AVR_USART(object_new(TYPE_AVR_USART)); + busdev =3D SYS_BUS_DEVICE(usart0); + sysbus_mmio_map(busdev, 0, OFFSET_DATA + USART_BASE); + /* + * These IRQ numbers don't match the datasheet because we're counting = from + * zero and not including reset. + */ + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(cpu_avr), 24)); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(DEVICE(cpu_avr), 25)); + sysbus_connect_irq(busdev, 2, qdev_get_gpio_in(DEVICE(cpu_avr), 26)); + usart0->prr_address =3D OFFSET_DATA + PRR0; + usart0->prr_mask =3D USART_PRR_MASK; + qdev_prop_set_chr(DEVICE(usart0), "chardev", serial_hd(0)); + object_property_set_bool(OBJECT(usart0), true, "realized", &error_fata= l); + + /* Timer 1 built-in periphal */ + timer1 =3D AVR_TIMER16(object_new(TYPE_AVR_TIMER16)); + busdev =3D SYS_BUS_DEVICE(timer1); + sysbus_mmio_map(busdev, 0, OFFSET_DATA + TIMER1_BASE); + sysbus_mmio_map(busdev, 1, OFFSET_DATA + TIMER1_IMSK_BASE); + sysbus_mmio_map(busdev, 2, OFFSET_DATA + TIMER1_IFR_BASE); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in( + DEVICE(cpu_avr), TIMER1_CAPT_IRQ)); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in( + DEVICE(cpu_avr), TIMER1_COMPA_IRQ)); + sysbus_connect_irq(busdev, 2, qdev_get_gpio_in( + DEVICE(cpu_avr), TIMER1_COMPB_IRQ)); + sysbus_connect_irq(busdev, 3, qdev_get_gpio_in( + DEVICE(cpu_avr), TIMER1_COMPC_IRQ)); + sysbus_connect_irq(busdev, 4, qdev_get_gpio_in( + DEVICE(cpu_avr), TIMER1_OVF_IRQ)); + timer1->prr_address =3D OFFSET_DATA + TIMER1_PRR; + timer1->prr_mask =3D TIMER1_PRR_MASK; + object_property_set_bool(OBJECT(timer1), true, "realized", &error_fata= l); + + /* Load firmware (contents of flash) trying to auto-detect format */ + firmware =3D machine->firmware; + if (firmware !=3D NULL) { + filename =3D qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware); + if (filename =3D=3D NULL) { + error_report("Unable to find %s", firmware); + exit(1); + } + + bytes_loaded =3D load_elf( + filename, NULL, NULL, NULL, NULL, NULL, NULL, 0, EM_NONE, 0, 0= ); + if (bytes_loaded < 0) { + error_report( + "Unable to load %s as ELF, trying again as raw binary", + firmware); + bytes_loaded =3D load_image_targphys( + filename, OFFSET_CODE, SIZE_FLASH); + } + if (bytes_loaded < 0) { + error_report( + "Unable to load firmware image %s as ELF or raw binary", + firmware); + exit(1); + } + } +} + +static void sample_machine_init(MachineClass *mc) +{ + mc->desc =3D "AVR sample/example board"; + mc->init =3D sample_init; + mc->is_default =3D 1; +} + +DEFINE_MACHINE("sample", sample_machine_init) --=20 2.21.0 From nobody Sat Apr 27 11:05:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=kent.ac.uk ARC-Seal: i=1; a=rsa-sha256; t=1556976199; cv=none; d=zoho.com; s=zohoarc; b=QP5WSKjqFa1QrUefQQ+Zikj7jPSJNyIS1pwnkMpYQTUxthn7n3IWlLrUXHT1DKpXam27Ez+0f7eg5wDfbWosOoSQbZS4++9y5iXUBmFleh6OoGmZ4iXs7YYWa1n1PCLTbZ+EPYli1CV8se3WkuFcsjf+xjX780aGjV1bNak+e0Y= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556976199; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=K5Xugf1xgStGFjlK7U6ESg5udaN3F843dNI98bAACPQ=; b=VD4KmBp6O/8p6yX7i9DEwz9WhOwFx0OToKk1zbghmyR/FDOGdrgIZzTQNldYtmdepr0PDY+a1HZqpvp5d07pr6Q9N4BTk+QwmNmvhboUoWhNifwdfoMQSy7sguEYG7ncusAbpu5Eh/liDFDWpxqcPa4OUPZ+7UG4F3H0RGKqUPU= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556976199136877.1442053668457; Sat, 4 May 2019 06:23:19 -0700 (PDT) Received: from localhost ([127.0.0.1]:56600 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMudI-0004er-4r for importer@patchew.org; Sat, 04 May 2019 09:23:16 -0400 Received: from eggs.gnu.org ([209.51.188.92]:44343) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMqDO-0007Wp-PI for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMqDM-0002wI-UU for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:14 -0400 Received: from mx0.kent.ac.uk ([129.12.21.32]:45969) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hMqDM-0002um-NE for qemu-devel@nongnu.org; Sat, 04 May 2019 04:40:12 -0400 Received: from banach.kent.ac.uk ([129.12.41.70]) by mx0.kent.ac.uk with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.91) (envelope-from ) id 1hMqDK-000Jin-F4; Sat, 04 May 2019 09:40:10 +0100 From: Sarah Harris To: qemu-devel@nongnu.org Date: Sat, 4 May 2019 09:36:38 +0100 Message-Id: <20190504083638.13380-9-S.E.Harris@kent.ac.uk> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> References: <20190504083638.13380-1-S.E.Harris@kent.ac.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 129.12.21.32 X-Mailman-Approved-At: Sat, 04 May 2019 09:20:02 -0400 Subject: [Qemu-devel] [PATCH v1 8/8] target/avr: Register AVR support with the rest of QEMU, the build system, and the MAINTAINERS file X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: S.E.Harris@kent.ac.uk, mrolnik@gmail.com, A.M.King@kent.ac.uk, E.J.C.Robbins@kent.ac.uk Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Signed-off-by: Sarah Harris --- MAINTAINERS | 6 ++++++ arch_init.c | 2 ++ configure | 6 ++++++ default-configs/avr-softmmu.mak | 5 +++++ include/disas/dis-asm.h | 6 ++++++ include/sysemu/arch_init.h | 1 + qapi/common.json | 2 +- target/avr/Makefile.objs | 23 +++++++++++++++++++++++ tests/machine-none-test.c | 1 + 9 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 default-configs/avr-softmmu.mak create mode 100644 target/avr/Makefile.objs diff --git a/MAINTAINERS b/MAINTAINERS index 7dd71e0a2d..859ceb2d08 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -162,6 +162,12 @@ S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* =20 +AVR +M: Michael Rolnik +S: Odd Fixes +F: target-avr/ +F: hw/avr/ + CRIS M: Edgar E. Iglesias S: Maintained diff --git a/arch_init.c b/arch_init.c index f4f3f610c8..184cdca6dd 100644 --- a/arch_init.c +++ b/arch_init.c @@ -86,6 +86,8 @@ int graphic_depth =3D 32; #define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_XTENSA) #define QEMU_ARCH QEMU_ARCH_XTENSA +#elif defined(TARGET_AVR) +#define QEMU_ARCH QEMU_ARCH_AVR #endif =20 const uint32_t arch_type =3D QEMU_ARCH; diff --git a/configure b/configure index 60719ddcc5..b8843059b5 100755 --- a/configure +++ b/configure @@ -7451,6 +7451,9 @@ case "$target_name" in target_compiler=3D$cross_cc_aarch64 eval "target_compiler_cflags=3D\$cross_cc_cflags_${target_name}" ;; + avr) + target_compiler=3D$cross_cc_avr + ;; cris) target_compiler=3D$cross_cc_cris ;; @@ -7726,6 +7729,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do disas_config "ARM_A64" fi ;; + avr) + disas_config "AVR" + ;; cris) disas_config "CRIS" ;; diff --git a/default-configs/avr-softmmu.mak b/default-configs/avr-softmmu.= mak new file mode 100644 index 0000000000..d1e1c28118 --- /dev/null +++ b/default-configs/avr-softmmu.mak @@ -0,0 +1,5 @@ +# Default configuration for avr-softmmu + +# Boards: +# +CONFIG_AVR_SAMPLE=3Dy diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 9240ec32c2..a7d230ba66 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -211,6 +211,12 @@ enum bfd_architecture #define bfd_mach_m32r 0 /* backwards compatibility */ bfd_arch_mn10200, /* Matsushita MN10200 */ bfd_arch_mn10300, /* Matsushita MN10300 */ + bfd_arch_avr, /* Atmel AVR microcontrollers. */ +#define bfd_mach_avr1 1 +#define bfd_mach_avr2 2 +#define bfd_mach_avr3 3 +#define bfd_mach_avr4 4 +#define bfd_mach_avr5 5 bfd_arch_cris, /* Axis CRIS */ #define bfd_mach_cris_v0_v10 255 #define bfd_mach_cris_v32 32 diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 10cbafe970..aff57bfe61 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -25,6 +25,7 @@ enum { QEMU_ARCH_NIOS2 =3D (1 << 17), QEMU_ARCH_HPPA =3D (1 << 18), QEMU_ARCH_RISCV =3D (1 << 19), + QEMU_ARCH_AVR =3D (1 << 20), }; =20 extern const uint32_t arch_type; diff --git a/qapi/common.json b/qapi/common.json index 99d313ef3b..eeacd0e3c2 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -187,7 +187,7 @@ # Since: 3.0 ## { 'enum' : 'SysEmuTarget', - 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', + 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', 'lm= 32', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', 'ppc64', 'riscv32', 'riscv64', 's390x', 'sh4', diff --git a/target/avr/Makefile.objs b/target/avr/Makefile.objs new file mode 100644 index 0000000000..41355dea1e --- /dev/null +++ b/target/avr/Makefile.objs @@ -0,0 +1,23 @@ +# +# QEMU AVR CPU +# +# Copyright (c) 2016 Michael Rolnik +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# +# + +obj-y +=3D translate.o cpu.o helper.o decode.o +obj-y +=3D gdbstub.o +obj-$(CONFIG_SOFTMMU) +=3D machine.o diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c index 4c6d470798..361927bb76 100644 --- a/tests/machine-none-test.c +++ b/tests/machine-none-test.c @@ -27,6 +27,7 @@ static struct arch2cpu cpus_map[] =3D { /* tested targets list */ { "arm", "cortex-a15" }, { "aarch64", "cortex-a57" }, + { "avr", "avr6" }, { "x86_64", "qemu64,apic-id=3D0" }, { "i386", "qemu32,apic-id=3D0" }, { "alpha", "ev67" }, --=20 2.21.0