Skip to content

Commit

Permalink
Added CSR support
Browse files Browse the repository at this point in the history
  • Loading branch information
Liu Yonggang committed Nov 8, 2022
1 parent 3e320bd commit 676c67b
Show file tree
Hide file tree
Showing 3 changed files with 362 additions and 11 deletions.
149 changes: 149 additions & 0 deletions src/csr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
from amaranth import *
from math import ceil, log2
from amaranth.sim import Simulator, Tick, Settle
from amaranth.back import *
from amaranth_soc.wishbone import *
from amaranth_soc.memory import *

import os, sys
import warnings
sys.path.append("..")

from src.isa import *

# CSR Addresses for the supported subset of 'Machine-Level ISA' CSRs.
# Machine information registers:
CSRA_MVENDORID = 0xF11
CSRA_MARCHID = 0xF12
CSRA_MIMPID = 0xF13
CSRA_MHARTID = 0xF14
# Machine trap setup:
CSRA_MSTATUS = 0x300
CSRA_MISA = 0x301
CSRA_MIE = 0x304
CSRA_MTVEC = 0x305
CSRA_MSTATUSH = 0x310
# Machine trap handling:
CSRA_MSCRATCH = 0x340
CSRA_MEPC = 0x341
CSRA_MCAUSE = 0x342
CSRA_MTVAL = 0x343
CSRA_MIP = 0x344
CSRA_MTINST = 0x34A
CSRA_MTVAL2 = 0x34B
# Machine counters:
CSRA_MCYCLE = 0xB00
CSRA_MINSTRET = 0xB02
# Machine counter setup:
CSRA_MCOUNTINHIBIT = 0x320

# CSR memory map definitions.
CSRS = {
'minstret': {
'c_addr': CSRA_MINSTRET,
'bits': { 'instrs': [ 0, 15, 'rw', 0 ] }
},
'mstatus': {
'c_addr': CSRA_MSTATUS,
'bits': {
'mie': [ 3, 3, 'rw', 0 ],
'mpie': [ 7, 7, 'r', 0 ]
}
},
'mcause': {
'c_addr': CSRA_MCAUSE,
'bits': {
'interrupt': [ 31, 31, 'rw', 0 ],
'ecode': [ 0, 30, 'rw', 0 ]
}
},
'mtval': {
'c_addr': CSRA_MTVAL,
'bits': { 'einfo': [ 0, 31, 'rw', 0 ] }
},
'mtvec': {
'c_addr': CSRA_MTVEC,
'bits': {
'mode': [ 0, 0, 'rw', 0 ],
'base': [ 2, 31, 'rw', 0 ]
}
},
'mepc': {
'c_addr': CSRA_MEPC,
'bits': {
'mepc': [ 2, 31, 'rw', 0 ]
}
},
}

#############################################
# 'Control and Status Registers' file. #
# This contains logic for handling the #
# 'system' opcode, which is used to #
# read/write CSRs in the base ISA. #
# CSR named constants are in `isa.py`. #
#############################################

# Core "CSR" class, which addresses Control and Status Registers.
class CSR( Elaboratable, Interface ):
def __init__( self ):
# CSR function select signal.
self.f = Signal( 3, reset = 0b000 )
# Actual data to write (depends on write/set/clear function)
self.wd = Signal( 32, reset = 0x00000000 )
# Initialize wishbone bus interface.
Interface.__init__( self, addr_width = 12, data_width = 32 )
self.memory_map = MemoryMap( addr_width = self.addr_width,
data_width = self.data_width,
alignment = 0 )
# Initialize required CSR signals and constants.
for cname, reg in CSRS.items():
for bname, bits in reg[ 'bits' ].items():
if 'w' in bits[ 2 ]:
setattr( self,
"%s_%s"%( cname, bname ),
Signal( bits[ 1 ] - bits[ 0 ] + 1,
name = "%s_%s"%( cname, bname ),
reset = bits[ 3 ] ) )
elif 'r' in bits[ 2 ]:
setattr( self,
"%s_%s"%( cname, bname ),
Const( bits[ 3 ] ) )

def elaborate( self, platform ):
m = Module()

# Read values default to 0.
m.d.comb += self.dat_r.eq( 0 )

with m.Switch( self.adr ):
# Generate logic for supported CSR reads / writes.
for cname, reg in CSRS.items():
with m.Case( reg['c_addr'] ):
# Assemble the read value from individual bitfields.
for bname, bits in reg[ 'bits' ].items():
if 'r' in bits[ 2 ]:
m.d.comb += self.dat_r \
.bit_select( bits[ 0 ], bits[ 1 ] - bits[ 0 ] + 1 ) \
.eq( getattr( self, "%s_%s"%( cname, bname ) ) )
with m.If( self.we == 1 ):
# Writes are enabled; set new values on the next tick.
if 'w' in bits[ 2 ]:
m.d.sync += getattr( self, "%s_%s"%( cname, bname ) ) \
.eq( self.wd[ bits[ 0 ] : ( bits[ 1 ] + 1 ) ] )

# Process 32-bit CSR write logic.
with m.If( ( self.f[ :2 ] ) == 0b01 ):
# 'Write' - set the register to the input value.
m.d.comb += self.wd.eq( self.dat_w )
with m.Elif( ( ( self.f[ :2 ] ) == 0b10 ) & ( self.dat_w != 0 ) ):
# 'Set' - set bits which are set in the input value.
m.d.comb += self.wd.eq( self.dat_w | self.dat_r )
with m.Elif( ( ( self.f[ :2 ] ) == 0b11 ) & ( self.dat_w != 0 ) ):
# 'Clear' - reset bits which are set in the input value.
m.d.comb += self.wd.eq( ~( self.dat_w ) & self.dat_r )
with m.Else():
# Read-only operation; set write data to current value.
m.d.comb += self.wd.eq( self.dat_r )

return m
96 changes: 85 additions & 11 deletions src/isa.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,92 @@

v_filename = "isa.v"

# Instruction field definition

#Opcode
OP_LUI = 0b0110011

#func3
F_ADD = 0b000

#func7
FF_ADD = 0b0000000

#CSR
# Instruction field definitions.
# RV32I opcode definitions:
OP_LUI = 0b0110111
OP_AUIPC = 0b0010111
OP_JAL = 0b1101111
OP_JALR = 0b1100111
OP_BRANCH = 0b1100011
OP_LOAD = 0b0000011
OP_STORE = 0b0100011
OP_REG = 0b0110011
OP_IMM = 0b0010011
OP_SYSTEM = 0b1110011
OP_FENCE = 0b0001111
# RV32I "funct3" bits. These select different functions with
# R-type, I-type, S-type, and B-type instructions.
F_JALR = 0b000
F_BEQ = 0b000
F_BNE = 0b001
F_BLT = 0b100
F_BGE = 0b101
F_BLTU = 0b110
F_BGEU = 0b111
F_LB = 0b000
F_LH = 0b001
F_LW = 0b010
F_LBU = 0b100
F_LHU = 0b101
F_SB = 0b000
F_SH = 0b001
F_SW = 0b010
F_ADDI = 0b000
F_SLTI = 0b010
F_SLTIU = 0b011
F_XORI = 0b100
F_ORI = 0b110
F_ANDI = 0b111
F_SLLI = 0b001
F_SRLI = 0b101
F_SRAI = 0b101
F_ADD = 0b000
F_SUB = 0b000
F_SLL = 0b001
F_SLT = 0b010
F_SLTU = 0b011
F_XOR = 0b100
F_SRL = 0b101
F_SRA = 0b101
F_OR = 0b110
F_AND = 0b111
# RV32I "funct7" bits. Along with the "funct3" bits, these select
# different functions with R-type instructions.
FF_SLLI = 0b0000000
FF_SRLI = 0b0000000
FF_SRAI = 0b0100000
FF_ADD = 0b0000000
FF_SUB = 0b0100000
FF_SLL = 0b0000000
FF_SLT = 0b0000000
FF_SLTU = 0b0000000
FF_XOR = 0b0000000
FF_SRL = 0b0000000
FF_SRA = 0b0100000
FF_OR = 0b0000000
FF_AND = 0b0000000
# CSR definitions, for 'ECALL' system instructions.
# Like with other "I-type" instructions, the 'funct3' bits select
# between different types of environment calls.
F_TRAPS = 0b000
F_CSRRW = 0b001
F_CSRRS = 0b010
F_CSRRC = 0b011
F_CSRRWI = 0b101
F_CSRRSI = 0b110
F_CSRRCI = 0b111
# Definitions for non-CSR 'ECALL' system instructions. These seem to
# use the whole 12-bit immediate to encode their functionality.
IMM_MRET = 0x302
IMM_WFI = 0x105
# ID numbers for different types of traps (exceptions).
TRAP_IMIS = 1
TRAP_ILLI = 2
TRAP_BREAK = 3
TRAP_LMIS = 4
TRAP_SMIS = 6
TRAP_ECALL = 11


#ALU operation definition
Expand Down
128 changes: 128 additions & 0 deletions test/test_csr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from amaranth import *
from math import ceil, log2
from amaranth.sim import Simulator, Tick, Settle
from amaranth.back import *
from amaranth_soc.memory import *
from amaranth_soc.wishbone import *

import os, sys
sys.path.append("..")

from src.isa import *
from src.csr import *

##################
# CSR testbench: #
##################
# Keep track of test pass / fail rates.
p = 0
f = 0


# Perform an individual CSR unit test.
def csr_ut( csr, reg, rin, cf, expected ):
global p, f
# Set address, write data, f.
yield csr.adr.eq( reg )
yield csr.dat_w.eq( rin )
yield csr.f.eq( cf )
# Wait a tick.
yield Tick()
# Check the result after combinatorial logic.
yield Settle()
actual = yield csr.dat_r
if hexs( expected ) != hexs( actual ):
f += 1
print( "\033[31mFAIL:\033[0m CSR 0x%03X = %s (got: %s)"
%( reg, hexs( expected ), hexs( actual ) ) )
else:
p += 1
print( "\033[32mPASS:\033[0m CSR 0x%03X = %s"
%( reg, hexs( expected ) ) )
# Set 'rw' and wait another tick.
yield csr.we.eq( 1 )
yield Tick()
yield Settle()
# Done. Reset rsel, rin, f, rw.
yield csr.adr.eq( 0 )
yield csr.dat_w.eq( 0 )
yield csr.f.eq( 0 )
yield csr.we.eq( 0 )

# Perform some basic CSR operation tests on a fully re-writable CSR.
def csr_rw_ut( csr, reg ):
# 'Set' with rin == 0 reads the value without writing.
yield from csr_ut( csr, reg, 0x00000000, F_CSRRS, 0x00000000 )
# 'Set Immediate' to set all bits.
yield from csr_ut( csr, reg, 0xFFFFFFFF, F_CSRRSI, 0x00000000 )
# 'Clear' to reset some bits.
yield from csr_ut( csr, reg, 0x01234567, F_CSRRC, 0xFFFFFFFF )
# 'Write' to set some bits and reset others.
yield from csr_ut( csr, reg, 0x0C0FFEE0, F_CSRRW, 0xFEDCBA98 )
# 'Write Immediate' to do the same thing.
yield from csr_ut( csr, reg, 0xFFFFFCBA, F_CSRRWI, 0x0C0FFEE0 )
# 'Clear Immediate' to clear all bits.
yield from csr_ut( csr, reg, 0xFFFFFFFF, F_CSRRCI, 0xFFFFFCBA )
# 'Clear' with rin == 0 reads the value without writing.
yield from csr_ut( csr, reg, 0x00000000, F_CSRRC, 0x00000000 )

# Top-level CSR test method.
def csr_test( csr ):
# Wait a tick and let signals settle after reset.
yield Settle()

# Print a test header.
print( "--- CSR Tests ---" )

# Test reading / writing 'MSTATUS' CSR. (Only 'MIE' can be written)
yield from csr_ut( csr, CSRA_MSTATUS, 0xFFFFFFFF, F_CSRRWI, 0x00000000 )
yield from csr_ut( csr, CSRA_MSTATUS, 0xFFFFFFFF, F_CSRRCI, 0x00000008 )
yield from csr_ut( csr, CSRA_MSTATUS, 0xFFFFFFFF, F_CSRRSI, 0x00000000 )
yield from csr_ut( csr, CSRA_MSTATUS, 0x00000000, F_CSRRW, 0x00000008 )
yield from csr_ut( csr, CSRA_MSTATUS, 0x00000000, F_CSRRS, 0x00000000 )
# Test reading / writing 'MTVEC' CSR. (R/W except 'MODE' >= 2)
yield from csr_ut( csr, CSRA_MTVEC, 0xFFFFFFFF, F_CSRRWI, 0x00000000 )
yield from csr_ut( csr, CSRA_MTVEC, 0xFFFFFFFF, F_CSRRCI, 0xFFFFFFFD )
yield from csr_ut( csr, CSRA_MTVEC, 0xFFFFFFFE, F_CSRRSI, 0x00000000 )
yield from csr_ut( csr, CSRA_MTVEC, 0x00000003, F_CSRRW, 0xFFFFFFFC )
yield from csr_ut( csr, CSRA_MTVEC, 0x00000000, F_CSRRS, 0x00000001 )
# Test reading / writing the 'MEPC' CSR. All bits except 0-1 R/W.
yield from csr_ut( csr, CSRA_MEPC, 0x00000000, F_CSRRS, 0x00000000 )
yield from csr_ut( csr, CSRA_MEPC, 0xFFFFFFFF, F_CSRRSI, 0x00000000 )
yield from csr_ut( csr, CSRA_MEPC, 0x01234567, F_CSRRC, 0xFFFFFFFC )
yield from csr_ut( csr, CSRA_MEPC, 0x0C0FFEE0, F_CSRRW, 0xFEDCBA98 )
yield from csr_ut( csr, CSRA_MEPC, 0xFFFFCBA9, F_CSRRW, 0x0C0FFEE0 )
yield from csr_ut( csr, CSRA_MEPC, 0xFFFFFFFF, F_CSRRCI, 0xFFFFCBA8 )
yield from csr_ut( csr, CSRA_MEPC, 0x00000000, F_CSRRS, 0x00000000 )

# Test reading / writing the 'MCAUSE' CSR.
yield from csr_rw_ut( csr, CSRA_MCAUSE )
# Test reading / writing the 'MTVAL' CSR.
yield from csr_rw_ut( csr, CSRA_MTVAL )

# Test an unrecognized CSR.
yield from csr_ut( csr, 0x101, 0x89ABCDEF, F_CSRRW, 0x00000000 )
yield from csr_ut( csr, 0x101, 0x89ABCDEF, F_CSRRC, 0x00000000 )
yield from csr_ut( csr, 0x101, 0x89ABCDEF, F_CSRRS, 0x00000000 )
yield from csr_ut( csr, 0x101, 0xFFFFCDEF, F_CSRRWI, 0x00000000 )
yield from csr_ut( csr, 0x101, 0xFFFFCDEF, F_CSRRCI, 0x00000000 )
yield from csr_ut( csr, 0x101, 0xFFFFCDEF, F_CSRRSI, 0x00000000 )

# Done.
yield Tick()
print( "CSR Tests: %d Passed, %d Failed"%( p, f ) )


# 'main' method to run a basic testbench.
if __name__ == "__main__":
# Instantiate a CSR module.
dut = CSR()
def proc():
yield from csr_test( dut )

# Run the tests.
sim = Simulator(dut)
sim.add_clock( 1e-6 )
sim.add_sync_process( proc )
with sim.write_vcd("csr.vcd"):
sim.run()

0 comments on commit 676c67b

Please sign in to comment.