Skip to content

Instantly share code, notes, and snippets.

@smunaut

smunaut/ISA.md Secret

Last active July 18, 2019 20:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smunaut/dac7a096add6bcdb5b70f0e205e4d16d to your computer and use it in GitHub Desktop.
Save smunaut/dac7a096add6bcdb5b70f0e205e4d16d to your computer and use it in GitHub Desktop.

Fire16 Soft Core for iCE40 FPGA

General architecture

  • 16 bit architecture

    • All pointers/addresses are word addresses
    • No concept of bytes / double-words / ... If required, it must be handled purely in software
  • Harvard architecture

    • Program space
    • Data space
    • IO space
    • Default configuration:
      • 1 SPRAM + small ROM for Program space giving 16k words
      • 1 EBR for Data giving 256 words
  • 32 General-Purpose Registers

    • In fact just windowed accesses into RAM
    • 2 banks of 16 registers
    • 'R' bank (R0 -> RF) : can only be either at the bottom or top of RAM
    • 'S' bank (S0 -> SF) : base address can be moved by increment of 4 words
    • Special opcode WIN is used to control the windows
  • Special registers

    • A accumulator, used for transfer, ALU operations, ...
    • X index register: used for indexed access into Program / Data / IO space.
      • Also used as a link register for function calls
    • Y index register: used for indexed access into Data / IO space
    • Indexed accesses can also include a 4 bit immediate offset and post-increment/decrement
    • MCSR provides read access to flags and control over register window
  • Processor flags:

    • Z: Zero
    • C: Carry
    • N: Negative
    • V: Overflow
  • Control flow

    • Jumps / Calls have a delay slot right after them (i.e. the next instruction is executed even if the branch is taken)
    • Only testing the Z flag is supported in conditional control flow. To support other conditions, a special op code can test other flags and assign the result to Z
  • Load / Store to Program memory

    • Instructions allow to freely load and store to the program memory
    • Those accesses are subject to several limitations and must be done very carefully
    • All accesses will result in a lost execution cycle hapenning 2 cycles after the access opcode.
    • If the opcode following the program memory access is a branch instruction, the opcode in the delay slot will not be executed if the branch is taken
      • Better avoid branches after those accesses all together
    • Load access won't have the result available directly. The next instruction can't use the A register as its value is undefined
    • Two accesses can't be done right after the other
    • All in all, unless being very careful, it's better to use a NOP right after any program memory load / store.
  • Load / Store to Data memory (including GPR accesses)

    • Reading a memory location that was written in the previous cycle will yield undefined results
  • Other 'gotchas':

    • Post-increment / Post-decrement are done in the 'Execute' stage, while operand value are fetched in the 'Decode' stage and so the updated value of the register will be availble 1 cycle late. If you don't use the same index register in two consecutive instructions, this is not an issue.
    • A special IMM instructions allows to extend any I-format op code to use 16 bit immediate. (use the 'IMM' instruction right before). It can also be used twice to add support for immediate operands to some opcode that would otherwise not support and immediate argument.

Boot process

Beause this CPU uses SPRAM as the main program memory and SPRAM cannot be initialized, the boot process is a bit special.

General idea is :

  • In addition to the SPRAM, a small 16 word ROM is mapped to the boot address.
  • This ROM will execute the FSBL (First Stage Boot Loader) that will copy the content of data RAM (which can be initialized) to address 0 of program memory and jump to it.
  • This program can either be the final application if it's small enough. or this can be a SSBL (Second Stage Boot Loader) that will load the real application from another medium like SPI flash

Instruction formats

I-fmt : Immediate format

,---------------------------------------------------------------,
| f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---------------------------------------------------------------|
|         opcode        | 1 | s |       imm8                    |
'---------------------------------------------------------------'

This format contains an 8 bit immediate field and a sign specifier. Depending on the actual instruction and if there was a special imm instruction executed before, the implicit I immediate register will either be a sign-extended version of the 9 bits value formed by s + imm8, or a combination of the two imm8 fields.

G-fmt : General Purpose Register format

,---------------------------------------------------------------,
| f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---------------------------------------------------------------|
|         opcode        | 0 | 0 | d |  src  |     reg           |
'---------------------------------------------------------------'

This instruction format is used when accessing General Purpose Registers.

  • reg: 5 bits used to indicate which register is to be accessed

    • The MSB selected the bank (0 = R bank, 1 = S bank)
    • The 4 LSBs select the actual register within the bank
  • src: Selects the operands for the operation to be performed

    • 00 = A, GPR
    • 01 = A, Immediate
    • 10 = GPR, A
    • 11 = GPR, Immediate
    • For operations with only 1 operand, only the first is applicable and only 01 and 11 are valid.
  • d: Selects the destination (if applicable)

    • 0 = A
    • 1 = GPR

S-fmt : Special Register format

,---------------------------------------------------------------,
| f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---------------------------------------------------------------|
|         opcode        | 0 | 1 |  mod  |  sel  |    imm 4      |
'---------------------------------------------------------------'
  • mod: Post operation to be applied to the special register.

    • 00: None
    • 01: Reserved
    • 10: Post-Increment
    • 11: Post-Decrement
    • This is only valid for X and Y registers and should be set to 00 for all other cases
  • sel: Select special register

    • 00: X
    • 01: Y
    • 10: MCSR (read-only) / WIN (write-only)
    • 11: BSWAP(A) (byte swapped version of A, read-only)
  • imm4: 4 bit immediate field. Meaning is dependent on opcode

OpCodes

Overview

Op Code Instruction class
00xxxx ALU 2 operands
01xxxx ALU 1 operand
10000x Program Memory access
10001x Data Memory access
1001xx IO access
101xxx Special
11xxxx Control Flow

ALU 2 operands ( 00xxxx )

Sub Op Mnemonic Description
0010 CMP Same as SUB but without writing result
0100 TEST Same as AND but without writing result
1000 ADD dst ← OpA + OpB
1001 ADDCY dst ← OpA + OpB + C
1010 SUB dst ← ~OpA + OpB + 1
1011 SUBCY dst ← ~OpA + OpB + C
1100 AND dst ← OpA & OpB
1101 ANDN dst ← ~OpA & OpB
1110 OR dst ← OpA | OpB
1111 XOR dst ← OpA ^ OpB

Note that the MSB is in fact a write enable and so any operation can be used to update the flag without writing the result if need-be.

The valid instruction formats are :

  • I-fmt: In this case dst and OpA is always A and OpB is always the immediate
  • G-fmt: In this case the OpA,OpB couple is specified by the src field and dst by the d field.

ALU 1 operand ( 01xxxx )

Sub Op Mnemonic Description
0000 INC dst ← OpA + 1
0001 DEC dst ← OpA - 1
1000 ROL dst ← RotateLeft(OpA)
1001 SL0 dst ← OpA << 1
1010 SL1 dst ← OpA << 1|1
1011 SLC dst ← OpA << 1|C
1100 ROR dst ← RotateRight(OpA)
1101 SR0 dst ← OpA >> 1
1110 SRA dst ← (OpA >> 1)|(OpA & (1 << 15))
1111 SRC dst ← (OpA >> 1)|(C << 15)

The valid instruction formats are :

  • G-fmt:
    • dst selected by the d field
    • OpA is the first operand from the src field with only 01 and 11 being valid
    • This means you can have A as both source and destination the the GPR is actually ignored.
  • S-fmt:
    • FIXME ... this should actually be possible to use [X] / [Y] as operand with minimal changes to the decode logic.
    • dst would always be the same as OpA for this combination

Program Memory access ( 10000x )

Store / Load between accumulator and program memory

Sub Op Mnemonic Description
0 LDP Load Program memory
1 STP Store Program memory

The valid instruction formats are :

  • I-fmt: Absolute address access.
  • S-fmt: Only X special register without offset is supported for indexed access

Data Memory access ( 10001x )

Store / Load between accumulator and data memory

Sub Op Mnemonic Description
0 LDD / MOV Load Data memory
1 STD / MOV Store Data memory

The valid instruction formats are :

  • I-fmt: Absolute address access.
  • S-fmt: Only X/Y special register supported for indexed access
  • G-fmt: In this case the mnemonic used is MOV and the order of operand is what selected between Store / Load

IO access ( 1001xx )

Read/Write between IO and accumulator

Sub Op Mnemonic Description
00 IORR IO Read Request
01 IORD IO Read Data
10 IOWR IO Write Request

The valid instruction formats are :

  • I-fmt: Absolute address access.
  • S-fmt: Only X/Y special register supported for indexed access

Special ( 101xxx )

MOVe between accumulator and Special Registers ( 1010ab )

For S-fmt:

The a and b bit control which is read/written.

  • a == 1 : A is written with the SPR value
  • b == 1 : SPR is written with A value
  • Both can be set at once to allow for atomic swaps

For I-fmt:

This is used to load constant in A register. ab must be 10.

WIN: Register Window control ( 101001 )

This instruction essentially a move from A to MCSR and is encoded as such. But the value of A is not simply written to the window, instead the imm4 field is used as an operation to apply:

Bit Name Description
3 win_R_ce Enable update of R window
2 win_R_rel Apply relative change to R window
1 win_S_ce Enable update of S window
0 win_S_rel Apply relative change to S window

Each window can either be left unaffected, or be updated with a new value. The new value can either be the value of A directly, or the old value added to the value of A.

IMM: Implicit Immediate register pre-loading ( 101100 )

Only I-fmt is supported (obviously ...)

CC: Extended Condition Code ( 101101 )

Only the I-fmt is supported with the LSB of the immediate indicating which extended condition code to use.

imm[2:0] Code Condition Description
000 mi Z = N Negative
001 vs Z = V Signed Overflow
010 ior Z = IO Req ready Is IO ready for a new request ?
011 iod Z = IO Read ready Is IO ready for read ?
100 cs/hs Z = C Carry Set
101 ge Z = (N == V) Signed greater than or equal
110 hi Z = (Z==0) && (C==1) Unsigned higher
111 gt Z = (Z==0) && (N==V) Signed greater than

Control Flow ( 11xxxx )

,---------------,
| d | c | b | a |
|---------------|
| l | r |  cc   |
'---------------'
  • l = Link. If set, X will be set to the return address.
    • Note that X is set regardless if the jump is executed or not !
  • r = Relative
  • cc = Condition Code
    • 00 - Never
    • 01 - Always
    • 10 - Z == 0
    • 11 - Z == 1

The mnemonic for the combinations are :

Sub Op Mnemonic Description
00xx BA / BAX Branch Absolute
01xx BR / BRX Branch Relative
10xx BAL / BALX Branch Absolute and Link
11xx BRL / BRLX Branch Relative and Link

The condition code are marked with a suffix. If no suffix is set, then the 'Always' condition code is assumed.

Code cc Description
nv 00 Never
al 01 Always (normally omitted)
z 10 Zero
nz 11 Non-Zero

The valid instruction formats are :

  • I-fmt: Immediate field is used for the jump target
  • S-fmt: Only mod=00, 'sel'=00 and imm4=0000 is supported.
    • Mnemonics are the ones with an X suffix
    • Uses the X register instead of immediate target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment