diff --git a/.gitignore b/.gitignore index 7e981ba..5d023a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build *.vcd +*.gtkw __pycache__ diff --git a/05_register_bank/bench.py b/05_register_bank/bench.py new file mode 100644 index 0000000..623a162 --- /dev/null +++ b/05_register_bank/bench.py @@ -0,0 +1,43 @@ +from amaranth import * +from amaranth.sim import * + +from soc import SOC + +soc = SOC() + +sim = Simulator(soc) + +prev_clk = 0 + +def proc(): + while True: + global prev_clk + clk = yield soc.slow_clk + if prev_clk == 0 and prev_clk != clk: + print("pc={}".format((yield soc.pc))) + print("instr={:#032b}".format((yield soc.instr))) + print("LEDS = {:05b}".format((yield soc.leds))) + if (yield soc.isALUreg): + print("ALUreg rd={} rs1={} rs2={} funct3={}".format( + (yield soc.rdId), (yield soc.rs1Id), (yield soc.rs2Id), + (yield soc.funct3))) + if (yield soc.isALUimm): + print("ALUimm rd={} rs1={} imm={} funct3={}".format( + (yield soc.rdId), (yield soc.rs1Id), (yield soc.Iimm), + (yield soc.funct3))) + if (yield soc.isLoad): + print("LOAD") + if (yield soc.isStore): + print("STORE") + if (yield soc.isSystem): + print("SYSTEM") + break + yield + prev_clk = clk + +sim.add_clock(1e-6) +sim.add_sync_process(proc) + +with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): + # Let's run for a quite long time + sim.run_until(2, ) diff --git a/05_register_bank/blink.py b/05_register_bank/blink.py new file mode 100644 index 0000000..518fef0 --- /dev/null +++ b/05_register_bank/blink.py @@ -0,0 +1,38 @@ +from amaranth import * +from amaranth_boards.arty_a7 import * + +from soc import SOC + +# A platform contains board specific information about FPGA pin assignments, +# toolchain and specific information for uploading the bitfile. +platform = ArtyA7_35Platform(toolchain="Symbiflow") + +# We need a top level module +m = Module() + +# This is the instance of our SOC +soc = SOC() + +# The SOC is turned into a submodule (fragment) of our top level module. +m.submodules.soc = soc + +# The platform allows access to the various resources defined by the board +# definition from amaranth-boards. +led0 = platform.request('led', 0) +led1 = platform.request('led', 1) +led2 = platform.request('led', 2) +led3 = platform.request('led', 3) +rgb = platform.request('rgb_led') + +# We connect the SOC leds signal to the various LEDs on the board. +m.d.comb += [ + led0.o.eq(soc.leds[0]), + led1.o.eq(soc.leds[1]), + led1.o.eq(soc.leds[2]), + led1.o.eq(soc.leds[3]), + rgb.r.o.eq(soc.leds[4]), +] + +# To generate the bitstream, we build() the platform using our top level +# module m. +platform.build(m, do_program=False) diff --git a/05_register_bank/soc.py b/05_register_bank/soc.py new file mode 100644 index 0000000..0e90118 --- /dev/null +++ b/05_register_bank/soc.py @@ -0,0 +1,135 @@ +import sys +from amaranth import * + +from clockworks import Clockworks + +class SOC(Elaboratable): + + def __init__(self): + + self.leds = Signal(5) + + # Signals in this list can easily be plotted as vcd traces + self.ports = [] + + def elaborate(self, platform): + + m = Module() + + cw = Clockworks(slow=21) + m.submodules.cw = cw + + # Instruction sequence to be executed + sequence = [ + # 24 16 8 0 + # .......|.......|.......|.......| + #R rs2 rs1 f3 rd op + #I imm rs1 f3 rd op + #S imm rs2 rs1 f3 imm op + # ......|....|....|..|....|......| + 0b00000000000000000000000000110011, # R add x0, x0, x0 + 0b00000000000000000000000010110011, # R add x1, x0, x0 + 0b00000000000100001000000010010011, # I addi x1, x1, 1 + 0b00000000000100001000000010010011, # I addi x1, x1, 1 + 0b00000000000100001000000010010011, # I addi x1, x1, 1 + 0b00000000000100001000000010010011, # I addi x1, x1, 1 + 0b00000000000000001010000100000011, # I lw x2, 0(x1) + 0b00000000000100010010000000100011, # S sw x2, 0(x1) + 0b00000000000100000000000001110011 # S ebreak + ] + + # Program counter + pc = Signal(32) + + # Current instruction + instr = Signal(32, reset=0b0110011) + + # Instruction memory initialised with above 'sequence' + mem = Array([Signal(32, reset=x) for x in sequence]) + + # Register bank + regs = Array([Signal(32) for x in range(32)]) + rs1 = Signal(32) + rs2 = Signal(32) + + writeBackData = C(0) + writeBackEn = C(0) + + # Opcode decoder + isALUreg = (instr[0:7] == 0b0110011) + isALUimm = (instr[0:7] == 0b0010011) + isBranch = (instr[0:7] == 0b1100011) + isJALR = (instr[0:7] == 0b1100111) + isJAL = (instr[0:7] == 0b1101111) + isAUIPC = (instr[0:7] == 0b0010111) + isLUI = (instr[0:7] == 0b0110111) + isLoad = (instr[0:7] == 0b0000011) + isStore = (instr[0:7] == 0b0100011) + isSystem = (instr[0:7] == 0b1110011) + + # Immediate format decoder + Uimm = (Cat(Repl(0, 12), instr[12:32])) + Iimm = (Cat(instr[20:31], Repl(instr[31], 21))) + Simm = (Cat(instr[7:12], Cat(instr[25:31], Repl(instr[31], 21)))), + Bimm = (Cat(0, Cat(instr[8:12], Cat(instr[25:31], Cat( + instr[7], Repl(instr[31], 20)))))) + Jimm = (Cat(0, Cat(instr[21:31], Cat(instr[20], Cat( + instr[12:20], Repl(instr[31], 12)))))) + + # Register addresses decoder + rs1Id = (instr[15:20]) + rs2Id = (instr[20:25]) + rdId = ( instr[7:12]) + + # Function code decdore + funct3 = (instr[12:15]) + funct7 = (instr[25:32]) + + # Data write back + with m.If(writeBackEn & (rdId != 0)): + m.d.slow += regs[rdId].eq(writeBackData) + + # Main state machine + with m.FSM(reset="FETCH_INSTR") as fsm: + # Assign important signals to LEDS + m.d.comb += self.leds.eq(Mux(isSystem, 31, (1 << fsm.state))) + with m.State("FETCH_INSTR"): + m.d.slow += instr.eq(mem[pc]) + m.next = "FETCH_REGS" + with m.State("FETCH_REGS"): + m.d.slow += [ + rs1.eq(regs[rs1Id]), + rs2.eq(regs[rs2Id]) + ] + m.next = "EXECUTE" + with m.State("EXECUTE"): + m.d.slow += pc.eq(pc + 1) + m.next = "FETCH_INSTR" + + + # Export signals for simulation + def export(signal, name): + if type(signal) is not Signal: + newsig = Signal(signal.shape(), name = name) + m.d.comb += newsig.eq(signal) + else: + newsig = signal + self.ports.append(newsig) + setattr(self, name, newsig) + + if platform is None: + export(ClockSignal("slow"), "slow_clk") + export(pc, "pc") + export(instr, "instr") + export(isALUreg, "isALUreg") + export(isALUimm, "isALUimm") + export(isLoad, "isLoad") + export(isStore, "isStore") + export(isSystem, "isSystem") + export(rdId, "rdId") + export(rs1Id, "rs1Id") + export(rs2Id, "rs2Id") + export(Iimm, "Iimm") + export(funct3, "funct3") + + return m