- Command line usage:
NEASM.py [-v/-b] [source_dir] <[targer_dir]>
- -v: full Verilog module output
- -b: bin output (not yet implemented)
- Empty lines are allowed
- immediate must be a valid Python integer literal or expression. Note that immediate must not contain any white space characters even if you put an expression!
- immediate will be ealuated with
eval()
without safety checking of any sort, so please don't write anything weird here :>- One trick is to write
LINE_NUMBER
at immediate field to get the current line number~
- One trick is to write
- Comments (starting with '
//
') can be put on an empty line or after a valid instruction - Don't use register 31 because it is reserved for the assembler for pseudo-instructions!
The NECPU is a 32-bit general purpose register architecture processor with the specifications below. Registers $rs
, $rt
, and $rd
are placeholders for actual general purpose registers $0
, $1
, $2
, ..., $31
, each holding a 32-bit value. immediate
refers to an immediate value (constant) and label
refers to label in the instruction corresponding to a specific line in the code. All immediates are given as 16-bit unsigned values. Whenever an immediate is used as an operand with a register as the other operand, the immediate is zero-extended to 32-bit before computation. All labels will be converted into actual addresses using direct addressing mode.
NECPU can accomodate up to 4294967296 (2^32) addressable memory where each memory location holds 4 bytes (32 bits). NECPU is word (4 bytes) addressable. (It is not byte addressable!)
Every instruction of NECPU takes exactly one clock cycle to complete. Hence, the time taken of a code snippet can easily be calculated.
We will use the following convension to simplify our description:
- Given a register
$r
, the content of the register$r
is given asR[$r]
. - Given a memory location
addr
, the content at the memory locationaddr
isM[addr]
. PC
is the program counter which is a special register that holds the address of the instruction currently being executed.
Instruction Format | Name | Operation |
---|---|---|
NOP | No-Op | Do nothing |
LW $rd , $rs , immediate |
Load Word | R[$rd] = M[R[$rs] + immediate] |
SW $rd , $rs , immediate |
Store Word | M[R[$rs] + immediate] = R[$rd] |
LLI $rd , immediate |
Load Lower Immediate | R[$rd][15:0] = immediate |
LUI $rd , immediate |
Load Upper Immediate | R[$rd][31:16] = immediate |
SLT $rd , $rs , $rt |
Set Less Than | R[$rd] = R[$rs] < R[$rt] |
SEQ $rd , $rs , $rt |
Set Equal | R[$rd] = R[$rs] == R[$rt] |
BEQ $rd , immediate |
Branch On Equal | PC = PC + (R[$rd] == immediate ? 2 : 1) |
BNE $rd , immediate |
Branch On Not Equal | PC = PC + (R[$rd] != immediate ? 2 : 1) |
ADD $rd , $rs , $rt |
Add | R[$rd] = R[$rs] + R[$rt] |
ADDi $rd , $rs , immediate |
Add immediate | R[$rd] = R[$rs] + immediate |
SUB $rd , $rs , $rt |
Subtract | R[$rd] = R[$rs] - R[$rt] |
SUBi $rd , $rs , immediate |
Subtract Immediate | R[$rd] = R[$rs] - immediate |
SLL $rd , $rs , $rt |
Shift Left Logical | R[$rd] = R[$rs] << R[$rt] |
SRL $rd , $rs , $rt |
Shift Right Logical | R[$rd] = R[$rs] >> R[$rt] |
AND $rd , $rs , $rt |
And | R[$rd] = R[$rs] & R[$rt] |
ANDi $rd , $rs , immediate |
And Immediate | R[$rd] = R[$rs] & immediate |
OR $rd , $rs , $rt |
Or | R[$rd] = R[$rs] |
ORi $rd , $rs , immediate |
Or Immediate | R[$rd] = R[$rs] |
INV $rd , $rs |
Invert | R[$rd] = ~ R[$rs] |
XOR $rd , $rs , $rt |
Xor | R[$rd] = R[$rs] ^ R[$rt] |
XORi $rd , $rs , immediate |
Xor Immediate | R[$rd] = R[$rs] ^ immediate |
JMP $rd |
Unconditional Jump | PC = R[$rd] |
- Type A
- SLT
- SEQ
- ADD
- SUB
- SLL
- SRL
- AND
- OR
- INV (
$rt
ignored) - XOR
- Type B
- LW
- SW
- LLI (
$rs
ignored) - LUI (
$rs
ignored) - BEQ (
$rs
ignored) - BNE (
$rs
ignored) - ADDi
- SUBi
- ANDi
- ORi
- XORi
- JMP (can also put under type A since only
$rd
is used)
Note that the BEQ
and BNE
instructions of NECPU differ from those of MIPS:
- NECPU's
BEQ
andBNE
instructions compare a register's value with animmediate
, while MIPS compares the values of two registers. - NECPU's
BEQ
andBNE
instructions can only skip the next one instruction if the condition is satisfied.
- JUMP (unstable)
JUMP [label]
JUMP
instruction complements the nativeJMP
instruction by accepting a label instead of a raw memory address. It simplifies the programming experience.- To create a label, put
[label]:
before the instruction you want to jump to. The label can be any valid string. - JUMP pseudo-instruction now supports jumping forward and backward!
- A
JUMP
pseudo-instruction is translated to the following 3 instructions:LUI $31 [Upper half of label's address]
LLI $31 [Lower half of label's address]
JMP $31
- Note that when a
JUMP
pseudo-instruction is placed right after a branch instruction, the first two LI instructions will be put before the branch instruction so that the branch instruction can choose whether to skip theJMP
instruction or not.
- LWI - Load Word Immediate
LWI $rd, immediate
LWI
basically combinesLLI
andLUI
immediate
here is a 32-bit constant value (zero-extended)
- Implement
.data
directive to support preloading data into the ROM.- Support loading external files into ROM (in the format of bin or bmp)
- LA (Load Address) pseudo-instruction to load the address of variable defined in the
.data
section
The disassembler is for the purpose of debugging only.
- Command line usage:
NEDISASM.py [verilog_instruction_rom_file_dir]
- The disassembled file
disasmout.asm
will be created in the current directory