Computer Architecture Lab/Winter2006/PoettschacherRosenblattlWolf/iMISC
- 1 iMISC - invertable Minimized Instruction Set Computer
iMISC - invertable Minimized Instruction Set Computer
The basic idea is to design a microprocessor that is able to perform all mandatory operations while reducing the usually rather rich RISC instruction set to a minimum.
- CPU Architecture: RISC
- Memory Architecture: Harvard
- Number of Opcodes: 14
- Number of Commands: 21
- Pipeline Stages: 4 + Writeback
- Branch Delay: 1 Cycle
- Command execution: 1 Tick
- Data Width: 16 bit
- Registers: 32 16-bit Registers (29 for general purpose)
- Addressable object size: 16-bit word
- Ram address width: 16 bit
- Ram size: up to 128 kb
- Rom address width: 16 bit
- Rom size: up to 128 kb
Specifications on Target (Altera Cyclone EP1C12Q240C8):
- Logic Cells: about 1100 (with UART, depending on ROM size)
- Total Memory Bits: 8704 + Rom
- f_max: about 84 MHz (with most optimizations, depending on ROM size)
To minimize the instruction set we decided to use only three bits for the opcode, limiting them to eight different opcodes. To keep the overall design simple, we have chosen to implement a load/store harvard architecture. Currently, the design specifies a 16 bit cpu with 16 bit address space and 32 16 bit registers, 29 of them are general purpose. Another simplification is the lack of a program status word which normally holds some flags like the carry flag or an overflow flag. Also, there is no dedicated stack pointer. Jumps are performed by writing to the program counter which is a special register.
The registers are numbered consecutively with a leading R character. The first register is R00. The three special registers are:
- R29 ... Zero register (ZERO)
- R30 ... Instruction Pointer (IP)
- R31 ... Previous Instruction Pointer (PIP)
They have the mnemonics ZERO, IP and PIP.
For the given design, the following instructions are mandatory:
- MV ... copys the value of one register to another register
- LD ... loads a value from the given address to the given register
- ST ... stores a value from a given register to the given address
- AND, OR, NAND or NOR ... performs a logical (N)AND/(N)OR operation on the two given registers and store the value to a given register
- ADD or SUB ... adds/subtracts the value of the second register to/from the first register
- LSL ... performs a left shift on a register
- LSR ... performs a right shift on a register
also, some form of conditional operation is needed, like
- SKIP ... skips next operation
Although this are just 7 different instructions, we need some more instructions by several reasons. First, we use a harvard architecture, thus we want to seperate access to the program memory (FLASH) from access to the data memory (SRAM). To simplify the load/store instructions, we limit them to the data memory, thus we need some way to get constants into a register as the data memory is not initialized. Therefore, we also need an instruction as
- LDI ... loads a constant to a given register.
Another design decision is that we want to fit every instruction into 16 bit. As three bit are taken by the opcode, 13 bits are left for the parameters. We use 32 registers, thus we need 5 bits per register. This limits the number of parameters to 2, if both parameters of an instruction are register numbers. So instruction like NAND write the result to one of the given source registers. Additionally the LDI instructions cant load a 16 bit constant, so we decided to limit the size of the constant parameter to 8 bit and added a high byte/low byte flag. This leaves only 4 bits left for the register number, so constants can only be loaded into the lower 16 registers. To speed up execution, the AND, OR and NOR instructions are a good idea, although they could be done by software using NAND operations.
We implemented access to peripherals as the LED or the UART memory-mapped in the address range from 0x0200 to 0x0203, with the following special addresses:
- 0x0200: The LED. If the bit 0 of this word is set, the LED goes on, otherwise, it goes off. A read has no effect.
- 0x0201: UART read. If a byte is received by the UART hardware, it is read at this address in the low byte, with the high byte set to 0x01. If no byte is available, this is always read as 0x0000. A write has no effect.
- 0x0202: UART write. If the UART write queue is not full, the lower byte of the word written here is transmitted. A read has no effect.
- 0x0203: UART status. Bit 0 signals the possiblity to write to the UART, bit 1 signals if a byte is ready to be received.
Finally, and not just to do something different than other designs, we enabled to invert instructions. This just means that by setting the inverted flag (by the SINV instruction), an opposite operation to the next instructions is performed, if possible. This enables to reduce the number of opcodes and enables further optimization within the pipeline. For example, the opposite operation of the LD instruction is the ST instruction. If the inverted flag is not set, only LD is possible, therefore no write back interferance within the pipeline is possible. If the iverted flag is set by the SINV instruction, all following LD instructions become ST instructions, therefore only write back operations may be possible.
Instruction Set of the iMISC architecture
|iNOP||iNOP||0000 000000000000||Incontrovertible NOP|
|SKIP||NOP||0001 000000000000||Skips the next Instruction or is ignored when inverted|
|LDI||LDI||001H/L CCCCCCCC RdRdRdRd||Loads C into the High/Low byte of Rd. Only the lower 16 Registers can be addressed by this.|
|LD||ST||0100 0 RsRsRsRsRs 0 RdRdRdRdRd||Loads the contents of the address given in Rs into Rd. When inverted, Rd is stored to the address given in Rs|
|MV||MV||0110 0 RsRsRsRsRs0 RdRdRdRdRd||Moves Rs to Rd|
|LSL||LSR||1000 0 RsRsRsRsRs 0 RdRdRdRdRd||Performs a logical left or right shift of Rs bits on Rd|
|CMP||CMP||1001 0 RsRsRsRsRs 0 RdRdRdRdRd||Compares Rs and Rd. Rs < Rd leads to Rd := -1, Rs = Rd to Rd := 0 and Rs > Rd to Rd := 1|
|NAND||AND||1010 0 RsRsRsRsRs 0 RdRdRdRdRd||Performs a logical NAND or AND of Rs and Rd and stores the result in Rd|
|NOR||OR||1011 0 RsRsRsRsRs 0 RdRdRdRdRd||Performs a logical NAND or AND of Rs and Rd and stores the result in Rd|
|ADD||SUB||1100 0 RsRsRsRsRs 0 RdRdRdRdRd||Adds the value in Rs to Rd or subtracts Rs from Rd|
|ADD (C)||SUB (C)||1101 CCCCCCC RdRdRdRdRd||Adds C to Rd or subtracts C from Rd|
|SINV||SINV||1110 0 RsRsRsRsRs 000000||Sets the invert flag when Rs is nonzero, cleares it otherwise|
|SINV (C)||SINV (C)||1111 00000000000 C||Sets the invert flag to the value of C|
As the mnemonics are used as the following:
- 0 ... one bit with value zero
- 1 ... one bit with value one
- C ... one bit of constant
- Rs ... one bit of the number of the source register
- Rd ... one bit of the number of the destination register
- H/L ... one bit signaling if high or low byte, H is equivalent to 1 and L to 0
The following Diagram shows the actual architecture: imisc_architecture.gif
The Assembler is the basic instrument to feed the iMISC architecture with machinecode. It is able to output text, binary or vhdl rom code. Also, it performs basic checks on the code. When using a C-Precompiler, assembler code becomes more readable as the iMISC-Assembler was not made to make code looking nice...
The assembler language is mostly comparable to the structure of the binary code, although there are some differences. Mainly, the assembler uses what it gets from the input file and converts it to the binary codeword which is written to the output. As the semantic of a codeword depends on the state of the InvertedFlag, it does not depend which of its representations is used. The basic syntax of an assembler command is as follows:
CMD SRC, DST
Here, CMD is the command name, SRC is the source may be a register or constant, depending on the command and DST is the destination and therefore again a register. Constants may be in decimal or hexadecimal format (either with leading 0x or h). The registers are named R00 to R31. The use of the special registers may be restricted depending on their meaning. R29 (ZERO) and R31 (PIP) must not be the destination register. R30, the instruction pointer register, may only be written through a move command where the source register is R00. Labels can be placed at the begin of a line and are delimited by a colon. They can be treated like constants (they are converted to 16-bit-addresses). The commands are as follows:
|SKIP||NOP||none||none||Skips the next Instruction or is ignored when inverted flag is set|
|LDI||LDI||8-bit-Constant||Register (R00-R15)||Loads C into the Low byte of Rd. Only the lower 16 Registers can be addressed by this.|
|LDI HIGH||LDI HIGH||(8-bit-Constant)||Register (R00-R15)||Loads C into the High byte of Rd. Only the lower 16 Registers can be addressed by this.|
|LD||ST||Register||Register||Loads the contents of the address given in SRC into DST. When inverted, SRC is stored to the address given in DST|
|MV||MV||Register||Register||Moves Rs to Rd|
|LSL||LSR||Register||Register||Performs a logical left or right shift on DEST by the number of bits given in SRC|
|CMP||CMP||Register||Register||Compares SRC and DEST similar to strcmp. DEST := -1 iff SRC<DEST or 0 iff SRC=DEST or 1 iff SRC>DEST|
|NAND||AND||Register||Register||Performs a logical NAND or AND of SRC and DST and stores the result in DST|
|NOR||OR||Register||Register||Performs a logical NAND or AND of SRC and DST and stores the result in DST|
|ADD||SUB||Register||Register||Adds the value in SRC to DST or subtracts SRC from DST|
|ADD||SUB||7-Bit-Constant||Register||Adds a 7-bit-Constant to DST or subtracts the constant from DST|
|SINV||SINV||Register||none||Sets the invert flag when SRC is nonzero, cleares it otherwise|
|SINV||SINV||1-Bit-Constant||none||Sets the invert flag to the value of the constant|
A very basic assembler file may look like that:
final: LDI HIGH(final), R00 LDI LOW(final), R00 MV R00,R30
This is a simple endless loop which should be placed at the end of every assembler file to prevent the processor to execute unwanted code.
As usual, semicolon can be used for comments. When using a C-Precompiler, some defines may be useful:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Convenience defines #define ZERO r29 #define JMP MV R00,R30 final: LDI HIGH(final), R00 LDI LOW(final), R00 JMP
$ ./as-iMISC.exe --help iMISC assembler, reads from stdin, writes to stdout switches: -t --text ... create human readable assembler output from input -b --binary ... create binary text from input -v --vhdl ... create VHDL output from input (default) To run the C preprocessor before assembling (recommended): gcc -x c -E -C -P <infile> | ./as-iMISC <switch>
All files related to this project are available at http://stud4.tuwien.ac.at/~e0325880/iMISC/. As there are: