Improved IRQ documentation, added assembler macros
This commit is contained in:
parent
792baeabcf
commit
818faffe25
6
Makefile
6
Makefile
|
@ -47,9 +47,9 @@ tests/%.o: tests/%.S tests/riscv_test.h tests/test_macros.h
|
||||||
-DTEST_FUNC_TXT='"$(notdir $(basename $<))"' -DTEST_FUNC_RET=$(notdir $(basename $<))_ret $<
|
-DTEST_FUNC_TXT='"$(notdir $(basename $<))"' -DTEST_FUNC_RET=$(notdir $(basename $<))_ret $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -vrf $(TEST_OBJS) firmware/firmware.elf firmware/firmware.bin firmware/firmware.hex \
|
rm -vrf $(FIRMWARE_OBJS) $(TEST_OBJS) \
|
||||||
firmware/firmware.map testbench*.exe testbench.vcd .Xil fsm_encoding.os \
|
firmware/firmware.{elf,bin,hex,map} \
|
||||||
synth_vivado.log synth_vivado_*.backup.log synth_vivado.v
|
testbench{,_sp,_axi}.exe testbench.vcd
|
||||||
|
|
||||||
.PHONY: test test_sp test_axi clean
|
.PHONY: test test_sp test_axi clean
|
||||||
|
|
||||||
|
|
66
README.md
66
README.md
|
@ -206,53 +206,51 @@ one bit is set in `q1`.
|
||||||
Registers `q2` and `q3` are uninitialized and can be used as temporary storage
|
Registers `q2` and `q3` are uninitialized and can be used as temporary storage
|
||||||
when saving/restoring register values in the IRQ handler.
|
when saving/restoring register values in the IRQ handler.
|
||||||
|
|
||||||
All of the following instructions are encoded under the `custom0` opcode.
|
All of the following instructions are encoded under the `custom0` opcode. The f3
|
||||||
|
and rs2 fields are ignored in all this instructions.
|
||||||
|
|
||||||
|
See [firmware/custom_ops.S](firmware/custom_ops.S) for GNU assembler macros that
|
||||||
|
implement mnemonics for this instructions.
|
||||||
|
|
||||||
|
See [firmware/start.S](firmware/start.S) for an example implementaion of an
|
||||||
|
interrupt handler assember wrapper, and [firmware/irq.c](firmware/irq.c) for
|
||||||
|
the actual interrupt handler.
|
||||||
|
|
||||||
#### getq rd, qs
|
#### getq rd, qs
|
||||||
|
|
||||||
This instruction copies the value from a q-register to a general-purpose
|
This instruction copies the value from a q-register to a general-purpose
|
||||||
register.
|
register.
|
||||||
|
|
||||||
0000000 00000 000XX 000 XXXXX 0001011
|
0000000 ----- 000XX --- XXXXX 0001011
|
||||||
f7 rs2 qs f3 rd opcode
|
f7 rs2 qs f3 rd opcode
|
||||||
|
|
||||||
Example assembler code using the `custom0` mnemonic:
|
Example:
|
||||||
|
|
||||||
| Instruction | Assember Code |
|
getq x5, q2
|
||||||
| ------------------| --------------------|
|
|
||||||
| getq x5, q2 | custom0 5, 2, 0, 0 |
|
|
||||||
| getq x3, q0 | custom0 3, 0, 0, 0 |
|
|
||||||
| getq x1, q3 | custom0 1, 3, 0, 0 |
|
|
||||||
|
|
||||||
#### setq qd, rs
|
#### setq qd, rs
|
||||||
|
|
||||||
This instruction copies the value from a general-purpose register to a
|
This instruction copies the value from a general-purpose register to a
|
||||||
q-register.
|
q-register.
|
||||||
|
|
||||||
0000001 00000 XXXXX 000 000XX 0001011
|
0000001 ----- XXXXX --- 000XX 0001011
|
||||||
f7 rs2 rs f3 qd opcode
|
f7 rs2 rs f3 qd opcode
|
||||||
|
|
||||||
Example assembler code using the `custom0` mnemonic:
|
Example:
|
||||||
|
|
||||||
| Instruction | Assember Code |
|
setq q2, x5
|
||||||
| ------------------| --------------------|
|
|
||||||
| setq q2, x5 | custom0 2, 5, 0, 1 |
|
|
||||||
| setq q0, x3 | custom0 0, 3, 0, 1 |
|
|
||||||
| setq q3, x1 | custom0 3, 1, 0, 1 |
|
|
||||||
|
|
||||||
#### retirq
|
#### retirq
|
||||||
|
|
||||||
Return from interrupt. This instruction copies the value from `q0`
|
Return from interrupt. This instruction copies the value from `q0`
|
||||||
to the program counter and re-enables interrupts.
|
to the program counter and re-enables interrupts.
|
||||||
|
|
||||||
0000010 00000 00000 000 00000 0001011
|
0000010 ----- 00000 --- 00000 0001011
|
||||||
f7 rs2 rs f3 rd opcode
|
f7 rs2 rs f3 rd opcode
|
||||||
|
|
||||||
Example assembler code using the `custom0` mnemonic:
|
Example:
|
||||||
|
|
||||||
| Instruction | Assember Code |
|
retirq
|
||||||
| ------------------| --------------------|
|
|
||||||
| retirq | custom0 0, 0, 0, 2 |
|
|
||||||
|
|
||||||
#### maskirq
|
#### maskirq
|
||||||
|
|
||||||
|
@ -260,14 +258,12 @@ The "IRQ Mask" register contains a bitmask of masked (disabled) interrupts.
|
||||||
This instruction writes a new value to the irq mask register and reads the old
|
This instruction writes a new value to the irq mask register and reads the old
|
||||||
value.
|
value.
|
||||||
|
|
||||||
0000011 00000 XXXXX 000 XXXXX 0001011
|
0000011 ----- XXXXX --- XXXXX 0001011
|
||||||
f7 rs2 rs f3 rd opcode
|
f7 rs2 rs f3 rd opcode
|
||||||
|
|
||||||
Example assembler code using the `custom0` mnemonic:
|
Example:
|
||||||
|
|
||||||
| Instruction | Assember Code |
|
maskirq x1, x2
|
||||||
| ------------------| --------------------|
|
|
||||||
| maskirq x1, x2 | custom0 1, 2, 0, 3 |
|
|
||||||
|
|
||||||
The processor starts with all interrupts disabled.
|
The processor starts with all interrupts disabled.
|
||||||
|
|
||||||
|
@ -276,17 +272,15 @@ interrupt is disabled will cause the processor to halt.
|
||||||
|
|
||||||
#### waitirq
|
#### waitirq
|
||||||
|
|
||||||
Pause execution until an interrupt triggers. The bitmask of pending IRQs is
|
Pause execution until an interrupt becomes pending. The bitmask of pending IRQs
|
||||||
written to `rd`.
|
is written to `rd`.
|
||||||
|
|
||||||
0000100 00000 00000 000 XXXXX 0001011
|
0000100 ----- 00000 --- XXXXX 0001011
|
||||||
f7 rs2 rs f3 rd opcode
|
f7 rs2 rs f3 rd opcode
|
||||||
|
|
||||||
Example assembler code using the `custom0` mnemonic:
|
Example:
|
||||||
|
|
||||||
| Instruction | Assember Code |
|
waitirq x1
|
||||||
| ------------------| --------------------|
|
|
||||||
| waitirq x1 | custom0 1, 0, 0, 4 |
|
|
||||||
|
|
||||||
#### timer
|
#### timer
|
||||||
|
|
||||||
|
@ -295,14 +289,12 @@ triggers the timer interrupt when transitioning from 1 to 0. Setting the
|
||||||
counter to zero disables the timer. The old value of the counter is written to
|
counter to zero disables the timer. The old value of the counter is written to
|
||||||
`rd`.
|
`rd`.
|
||||||
|
|
||||||
0000101 00000 XXXXX 000 XXXXX 0001011
|
0000101 ----- XXXXX --- XXXXX 0001011
|
||||||
f7 rs2 rs f3 rd opcode
|
f7 rs2 rs f3 rd opcode
|
||||||
|
|
||||||
Example assembler code using the `custom0` mnemonic:
|
Example:
|
||||||
|
|
||||||
| Instruction | Assember Code |
|
timer x1, x2
|
||||||
| ------------------| --------------------|
|
|
||||||
| timer x1, x2 | custom0 1, 2, 0, 5 |
|
|
||||||
|
|
||||||
|
|
||||||
Building a pure RV32I Toolchain:
|
Building a pure RV32I Toolchain:
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
|
#define q0 0
|
||||||
|
#define q1 1
|
||||||
|
#define q2 2
|
||||||
|
#define q3 3
|
||||||
|
|
||||||
|
.macro getq rd qs
|
||||||
|
custom0 \rd,\qs,0,0
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro setq qd rs
|
||||||
|
custom0 \qd,\rs,0,1
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro retirq
|
||||||
|
custom0 0,0,0,2
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro maskirq rd rs
|
||||||
|
custom0 \rd,\rs,0,3
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro waitirq rd
|
||||||
|
custom0 \rd,0,0,4
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro timer rd rs
|
||||||
|
custom0 \rd,\rs,0,5
|
||||||
|
.endm
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
#ifndef FIRMWARE_H
|
#ifndef FIRMWARE_H
|
||||||
#define FIRMWARE_H
|
#define FIRMWARE_H
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
#include "firmware.h"
|
#include "firmware.h"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
# This is free and unencumbered software released into the public domain.
|
||||||
|
#
|
||||||
|
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
# distribute this software, either in source code form or as a compiled
|
||||||
|
# binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
# means.
|
||||||
|
|
||||||
from sys import argv
|
from sys import argv
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
#include "firmware.h"
|
#include "firmware.h"
|
||||||
|
|
||||||
uint32_t xorshift32() {
|
uint32_t xorshift32() {
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
#include "firmware.h"
|
#include "firmware.h"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
/*
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
*/
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
.memory : {
|
.memory : {
|
||||||
. = 0x000000;
|
. = 0x000000;
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
// A simple Sieve of Eratosthenes
|
// A simple Sieve of Eratosthenes
|
||||||
|
|
||||||
#include "firmware.h"
|
#include "firmware.h"
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
#define ENABLE_RVTST
|
#define ENABLE_RVTST
|
||||||
#define ENABLE_SIEVE
|
#define ENABLE_SIEVE
|
||||||
#define ENABLE_MULTST
|
#define ENABLE_MULTST
|
||||||
#define ENABLE_STATS
|
#define ENABLE_STATS
|
||||||
|
|
||||||
|
#include "custom_ops.S"
|
||||||
|
|
||||||
.section .text
|
.section .text
|
||||||
.global irq
|
.global irq
|
||||||
.global sieve
|
.global sieve
|
||||||
|
@ -13,43 +22,33 @@
|
||||||
.global hard_mulhu
|
.global hard_mulhu
|
||||||
.global stats
|
.global stats
|
||||||
|
|
||||||
#ifdef ENABLE_RVTST
|
|
||||||
# define TEST(n) \
|
|
||||||
.global n; \
|
|
||||||
addi x1, zero, 1000; \
|
|
||||||
custom0 0,1,0,5; /* timer zero, x1 */ \
|
|
||||||
jal zero,n; \
|
|
||||||
.global n ## _ret; \
|
|
||||||
n ## _ret:
|
|
||||||
#else
|
|
||||||
# define TEST(n) \
|
|
||||||
.global n ## _ret; \
|
|
||||||
n ## _ret:
|
|
||||||
#endif
|
|
||||||
|
|
||||||
reset_vec:
|
reset_vec:
|
||||||
custom0 0,0,0,4 // waitirq zero
|
// no more than 16 bytes here !
|
||||||
custom0 0,0,0,3 // maskirq zero, zero
|
waitirq zero
|
||||||
|
maskirq zero, zero
|
||||||
j start
|
j start
|
||||||
nop
|
|
||||||
nop
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Interrupt handler
|
||||||
|
**********************************/
|
||||||
|
|
||||||
|
.balign 16
|
||||||
irq_vec:
|
irq_vec:
|
||||||
/* save registers */
|
/* save registers */
|
||||||
|
|
||||||
custom0 2,1,0,1 // setq q2, x1
|
setq q2, x1
|
||||||
custom0 3,2,0,1 // setq q3, x2
|
setq q3, x2
|
||||||
|
|
||||||
lui x1, %hi(irq_regs)
|
lui x1, %hi(irq_regs)
|
||||||
addi x1, x1, %lo(irq_regs)
|
addi x1, x1, %lo(irq_regs)
|
||||||
|
|
||||||
custom0 2,0,0,0 // getq x2, q0
|
getq x2, q0
|
||||||
sw x2, 0*4(x1)
|
sw x2, 0*4(x1)
|
||||||
|
|
||||||
custom0 2,2,0,0 // getq x2, q2
|
getq x2, q2
|
||||||
sw x2, 1*4(x1)
|
sw x2, 1*4(x1)
|
||||||
|
|
||||||
custom0 2,3,0,0 // getq x2, q3
|
getq x2, q3
|
||||||
sw x2, 2*4(x1)
|
sw x2, 2*4(x1)
|
||||||
|
|
||||||
sw x3, 3*4(x1)
|
sw x3, 3*4(x1)
|
||||||
|
@ -82,19 +81,19 @@ irq_vec:
|
||||||
sw x30, 30*4(x1)
|
sw x30, 30*4(x1)
|
||||||
sw x31, 31*4(x1)
|
sw x31, 31*4(x1)
|
||||||
|
|
||||||
/* call interrupt handler */
|
/* call interrupt handler C function */
|
||||||
|
|
||||||
lui sp, %hi(irq_stack_top)
|
lui sp, %hi(irq_stack)
|
||||||
addi sp, sp, %lo(irq_stack_top)
|
addi sp, sp, %lo(irq_stack)
|
||||||
|
|
||||||
// arg0 = address of regs
|
// arg0 = address of regs
|
||||||
lui a0, %hi(irq_regs)
|
lui a0, %hi(irq_regs)
|
||||||
addi a0, a0, %lo(irq_regs)
|
addi a0, a0, %lo(irq_regs)
|
||||||
|
|
||||||
// arg1 = interrupt type
|
// arg1 = interrupt type
|
||||||
custom0 11,1,0,0 // getq x11, q1
|
getq x11, q1
|
||||||
|
|
||||||
// call to c function
|
// call to C function
|
||||||
jal ra, irq
|
jal ra, irq
|
||||||
|
|
||||||
/* restore registers */
|
/* restore registers */
|
||||||
|
@ -103,13 +102,13 @@ irq_vec:
|
||||||
addi x1, x1, %lo(irq_regs)
|
addi x1, x1, %lo(irq_regs)
|
||||||
|
|
||||||
lw x2, 0*4(x1)
|
lw x2, 0*4(x1)
|
||||||
custom0 0,2,0,1 // setq q0, x2
|
setq q0, x2
|
||||||
|
|
||||||
lw x2, 1*4(x1)
|
lw x2, 1*4(x1)
|
||||||
custom0 1,2,0,1 // setq q1, x2
|
setq q1, x2
|
||||||
|
|
||||||
lw x2, 2*4(x1)
|
lw x2, 2*4(x1)
|
||||||
custom0 2,2,0,1 // setq q2, x2
|
setq q2, x2
|
||||||
|
|
||||||
lw x3, 3*4(x1)
|
lw x3, 3*4(x1)
|
||||||
lw x4, 4*4(x1)
|
lw x4, 4*4(x1)
|
||||||
|
@ -141,21 +140,27 @@ irq_vec:
|
||||||
lw x30, 30*4(x1)
|
lw x30, 30*4(x1)
|
||||||
lw x31, 31*4(x1)
|
lw x31, 31*4(x1)
|
||||||
|
|
||||||
custom0 1,1,0,0 // getq x1, q1
|
getq x1, q1
|
||||||
custom0 2,2,0,0 // getq x2, q2
|
getq x2, q2
|
||||||
|
|
||||||
custom0 0,0,0,2 // retirq
|
retirq
|
||||||
|
|
||||||
irq_regs:
|
irq_regs:
|
||||||
// registers are saved to this memory region
|
// registers are saved to this memory region during interrupt handling
|
||||||
// the program counter is saved as register 0
|
// the program counter is saved as register 0
|
||||||
.fill 32,4
|
.fill 32,4
|
||||||
|
|
||||||
irq_stack:
|
// stack for the interrupt handler
|
||||||
.fill 128,4
|
.fill 128,4
|
||||||
irq_stack_top:
|
irq_stack:
|
||||||
|
|
||||||
|
|
||||||
|
/* Main program
|
||||||
|
**********************************/
|
||||||
|
|
||||||
start:
|
start:
|
||||||
|
/* zero-initialize all registers */
|
||||||
|
|
||||||
addi x1, zero, 0
|
addi x1, zero, 0
|
||||||
addi x2, zero, 0
|
addi x2, zero, 0
|
||||||
addi x3, zero, 0
|
addi x3, zero, 0
|
||||||
|
@ -188,6 +193,22 @@ start:
|
||||||
addi x30, zero, 0
|
addi x30, zero, 0
|
||||||
addi x31, zero, 0
|
addi x31, zero, 0
|
||||||
|
|
||||||
|
/* running tests from riscv-tests */
|
||||||
|
|
||||||
|
#ifdef ENABLE_RVTST
|
||||||
|
# define TEST(n) \
|
||||||
|
.global n; \
|
||||||
|
addi x1, zero, 1000; \
|
||||||
|
timer zero, x1; \
|
||||||
|
jal zero,n; \
|
||||||
|
.global n ## _ret; \
|
||||||
|
n ## _ret:
|
||||||
|
#else
|
||||||
|
# define TEST(n) \
|
||||||
|
.global n ## _ret; \
|
||||||
|
n ## _ret:
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(lui)
|
TEST(lui)
|
||||||
TEST(auipc)
|
TEST(auipc)
|
||||||
TEST(j)
|
TEST(j)
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
|
||||||
#include "firmware.h"
|
#include "firmware.h"
|
||||||
|
|
||||||
static void stats_print_dec(int val, int digits, bool zero_pad)
|
static void stats_print_dec(int val, int digits, bool zero_pad)
|
||||||
|
|
Loading…
Reference in New Issue