├── .github └── FUNDING.yml ├── env.sh ├── .gitignore ├── src ├── start.S ├── wait.S ├── bram.ld ├── blinker.S └── Makefile ├── 19_verilator ├── snippet.v ├── sim_main.cpp └── run_verilator.sh ├── 01_blink ├── bench.py └── soc.py ├── 02_slower_blinky ├── bench.py └── soc.py ├── 03_blink_from_rom ├── bench.py ├── soc.py └── blink.py ├── boards ├── sipeed_tangnano9k.py ├── digilent_cmod_a7.py ├── digilent_cmod_s7.py ├── digilent_arty_a7.py └── top.py ├── 11_modules ├── memory.py ├── blink.py ├── bench.py ├── soc.py └── cpu.py ├── 12_size_optimisation ├── memory.py ├── blink.py ├── bench.py ├── soc.py └── cpu.py ├── tests ├── bench.py ├── blink.py ├── memory.py └── soc.py ├── 14_subroutines_v2 ├── memory.py ├── blink.py ├── bench.py ├── soc.py └── cpu.py ├── 13_subroutines ├── memory.py ├── blink.py ├── bench.py ├── soc.py └── cpu.py ├── 06_alu ├── blink.py ├── bench.py └── soc.py ├── 15_load ├── blink.py ├── memory.py ├── bench.py ├── soc.py └── cpu.py ├── 07_assembler ├── blink.py ├── bench.py └── soc.py ├── 08_jumps ├── blink.py ├── bench.py └── soc.py ├── 09_branches ├── blink.py ├── bench.py └── soc.py ├── 10_lui_auipc ├── blink.py ├── bench.py └── soc.py ├── 16_store ├── blink.py ├── bench.py ├── memory.py └── soc.py ├── 05_register_bank ├── blink.py ├── bench.py └── soc.py ├── 17_memory_map ├── blink.py ├── bench.py ├── memory.py └── soc.py ├── 18_mandelbrot ├── blink.py ├── bench.py ├── soc.py └── memory.py ├── 04_instruction_decoder ├── blink.py ├── bench.py └── soc.py ├── LICENSES └── BSD-3-Clause ├── tools └── elf2hex.py ├── lib ├── uart_tx.py └── clockworks.py └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: bl0x_ 2 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | export PYTHONPATH=$(pwd)/lib:$(pwd)/tools 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.vcd 3 | *.gtkw 4 | __pycache__ 5 | obj_dir 6 | bugs 7 | *.o 8 | *.bram.elf 9 | -------------------------------------------------------------------------------- /src/start.S: -------------------------------------------------------------------------------- 1 | .equ IO_BASE, 0x400000 2 | .section .text 3 | .globl start 4 | start: 5 | li gp,IO_BASE 6 | li sp,0x1800 7 | call main 8 | ebreak 9 | -------------------------------------------------------------------------------- /src/wait.S: -------------------------------------------------------------------------------- 1 | .section .text 2 | .globl wait 3 | 4 | wait: 5 | li t0,1 6 | slli t0, t0,17 7 | .L0: 8 | addi t0,t0,-1 9 | bnez t0, .L0 10 | ret 11 | 12 | -------------------------------------------------------------------------------- /src/bram.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | BRAM (RWX) : ORIGIN = 0x0000, LENGTH = 0x1800 /* 6kB RAM */ 4 | } 5 | SECTIONS 6 | { 7 | everything : 8 | { 9 | . = ALIGN(4); 10 | start.o (.text) 11 | *(.*) 12 | } >BRAM 13 | } 14 | -------------------------------------------------------------------------------- /19_verilator/snippet.v: -------------------------------------------------------------------------------- 1 | // add this to the end of the 'top' module in build/top.v 2 | 3 | `ifdef BENCH 4 | always @(posedge clk) 5 | begin 6 | if(uart_valid) 7 | begin 8 | $write("%c", memory_mem_wdata[7:0] ); 9 | $fflush(32'h8000_0001); 10 | end 11 | end 12 | `endif 13 | -------------------------------------------------------------------------------- /19_verilator/sim_main.cpp: -------------------------------------------------------------------------------- 1 | #include "Vsoc.h" 2 | #include "verilated.h" 3 | #include 4 | 5 | int 6 | main(int argc, char **argv, char **env) 7 | { 8 | Vsoc top; 9 | top.clk = 0; 10 | while (!Verilated::gotFinish()) { 11 | top.clk = !top.clk; 12 | top.eval(); 13 | } 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/blinker.S: -------------------------------------------------------------------------------- 1 | # Simple blinker 2 | 3 | .equ IO_BASE, 0x400000 4 | .equ IO_LEDS, 4 5 | 6 | .section .text 7 | 8 | .globl main 9 | 10 | main: 11 | .L0: 12 | 13 | li t0, 5 14 | sw t0, IO_LEDS(gp) 15 | call wait 16 | li t0, 10 17 | sw t0, IO_LEDS(gp) 18 | call wait 19 | j .L0 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | all: blinker.bram.elf 2 | 3 | AS := riscv64-unknown-elf-as 4 | LD := riscv64-unknown-elf-ld 5 | ASFLAGS := -march=rv32i -mabi=ilp32 -mno-relax 6 | LDFLAGS := -T bram.ld -m elf32lriscv -nostdlib -norelax 7 | 8 | %.o: %.S 9 | $(AS) $(ASFLAGS) $< -o $@ 10 | 11 | blinker.bram.elf: start.o blinker.o wait.o 12 | $(LD) $(LDFLAGS) $^ -o $@ 13 | 14 | clean: 15 | rm *.o 16 | rm *.bram.elf 17 | -------------------------------------------------------------------------------- /01_blink/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth.sim import Simulator 2 | 3 | from soc import SOC 4 | 5 | soc = SOC() 6 | 7 | prev_leds = 0 8 | 9 | async def testbench(ctx): 10 | global prev_leds 11 | while True: 12 | leds = ctx.get(soc.leds) 13 | if leds != prev_leds: 14 | print("LEDS = {:05b}".format(leds)) 15 | prev_leds = leds 16 | await ctx.tick() 17 | 18 | sim = Simulator(soc) 19 | sim.add_clock(1e-6) 20 | sim.add_testbench(testbench) 21 | 22 | with sim.write_vcd('bench.vcd'): 23 | sim.run_until(2e-5) 24 | -------------------------------------------------------------------------------- /02_slower_blinky/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth.sim import Simulator 2 | 3 | from soc import SOC 4 | 5 | soc = SOC() 6 | 7 | prev_leds = 0 8 | 9 | async def testbench(ctx): 10 | global prev_leds 11 | while True: 12 | leds = ctx.get(soc.leds) 13 | if leds != prev_leds: 14 | print("LEDS = {:05b}".format(leds)) 15 | prev_leds = leds 16 | await ctx.tick() 17 | 18 | sim = Simulator(soc) 19 | sim.add_clock(1e-6) 20 | sim.add_testbench(testbench) 21 | 22 | with sim.write_vcd('bench.vcd'): 23 | # Let's run for a quite long time 24 | sim.run_until(2) 25 | -------------------------------------------------------------------------------- /03_blink_from_rom/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_leds = 0 11 | 12 | def proc(): 13 | global prev_leds 14 | while True: 15 | leds = yield soc.leds 16 | if leds != prev_leds: 17 | print("LEDS = {:05b}".format(leds)) 18 | prev_leds = leds 19 | yield 20 | 21 | sim.add_clock(1e-6) 22 | sim.add_sync_process(proc) 23 | 24 | with sim.write_vcd('bench.vcd'): 25 | # Let's run for a quite long time 26 | sim.run_until(2) 27 | -------------------------------------------------------------------------------- /19_verilator/run_verilator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "Make sure:" 6 | echo " - you've compiled a design previously, so build/top.v exists" 7 | echo " - you've added the snippet.v to the 'top' module in build/top.v" 8 | echo "" 9 | read -n 1 -s -r -p "Press any key to continue or Ctrl-C to stop." 10 | 11 | cd 19_verilator/.. || cd ../19_verilator/.. 12 | 13 | # generate C++ sources in 'obj_dir' 14 | verilator \ 15 | -DBENCH \ 16 | -DBOARD_FREQ=12 \ 17 | -Wno-fatal \ 18 | --top-module soc \ 19 | -cc -exe \ 20 | 19_verilator/sim_main.cpp \ 21 | build/top.v 22 | 23 | # compile C++ sources 24 | make -C obj_dir -j8 -f Vsoc.mk 25 | 26 | # run the simulation 27 | ./obj_dir/Vsoc 28 | -------------------------------------------------------------------------------- /boards/sipeed_tangnano9k.py: -------------------------------------------------------------------------------- 1 | from amaranth_boards.tang_nano_9k import * 2 | from amaranth.build import * 3 | 4 | from top import Top 5 | 6 | if __name__ == "__main__": 7 | platform = TangNano9kPlatform(toolchain="Gowin") # toolchain = (Gowin, Apicula) 8 | 9 | # The platform allows access to the various resources defined by the board 10 | # definition from amaranth-boards. 11 | led0 = platform.request('led', 0) 12 | led1 = platform.request('led', 1) 13 | led2 = platform.request('led', 2) 14 | led3 = platform.request('led', 3) 15 | led4 = platform.request('led', 4) 16 | leds = [led0, led1, led2, led3, led4] 17 | uart = platform.request('uart', 0) 18 | 19 | platform.build(Top(leds, uart), do_program=True) 20 | 21 | -------------------------------------------------------------------------------- /boards/digilent_cmod_a7.py: -------------------------------------------------------------------------------- 1 | from amaranth.build import * 2 | from amaranth_boards.cmod_a7 import * 3 | 4 | from top import Top 5 | 6 | if __name__ == "__main__": 7 | platform = CmodA7_35Platform(toolchain="Symbiflow") 8 | gpio = ("gpio", 0) 9 | platform.add_resources([ 10 | Resource("uart", 1, 11 | Subsignal("tx", Pins("1", conn=gpio, dir='o')), 12 | Subsignal("rx", Pins("2", conn=gpio, dir='i')), 13 | Attrs(IOSTANDARD="LVCMOS33") 14 | ) 15 | ]) 16 | 17 | # The platform allows access to the various resources defined 18 | # by the board definition from amaranth-boards. 19 | led0 = platform.request('led', 0) 20 | led1 = platform.request('led', 1) 21 | rgb = platform.request('rgb_led') 22 | leds = [led0, led1, rgb.r, rgb.g, rgb.b] 23 | uart = platform.request('uart', 1) 24 | 25 | platform.build(Top(leds, uart), do_program=True) 26 | -------------------------------------------------------------------------------- /boards/digilent_cmod_s7.py: -------------------------------------------------------------------------------- 1 | from amaranth.build import * 2 | from amaranth_boards.cmod_s7 import * 3 | 4 | from top import Top 5 | 6 | if __name__ == "__main__": 7 | platform = CmodS7_Platform(toolchain="Symbiflow") 8 | gpio = ("gpio", 0) 9 | platform.add_resources([ 10 | Resource("uart", 1, 11 | Subsignal("tx", Pins("1", conn=gpio, dir='o')), 12 | Subsignal("rx", Pins("2", conn=gpio, dir='i')), 13 | Attrs(IOSTANDARD="LVCMOS33") 14 | ) 15 | ]) 16 | 17 | # The platform allows access to the various resources defined 18 | # by the board definition from amaranth-boards. 19 | led0 = platform.request('led', 0) 20 | led1 = platform.request('led', 1) 21 | rgb = platform.request('rgb_led') 22 | leds = [led0, led1, rgb.r, rgb.g, rgb.b] 23 | uart = platform.request('uart', 1) 24 | 25 | platform.build(Top(leds, uart), do_program=True) 26 | -------------------------------------------------------------------------------- /03_blink_from_rom/soc.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | from clockworks import Clockworks 4 | 5 | class SOC(Elaboratable): 6 | 7 | def __init__(self): 8 | 9 | self.leds = Signal(5) 10 | 11 | def elaborate(self, platform): 12 | 13 | m = Module() 14 | 15 | cw = Clockworks(m, slow=21) 16 | m.submodules.cw = cw 17 | 18 | sequence = [ 19 | 0b00000, 20 | 0b00001, 21 | 0b00010, 22 | 0b00100, 23 | 0b01000, 24 | 0b10000, 25 | 0b10001, 26 | 0b10010, 27 | 0b10100, 28 | 0b11000, 29 | ] 30 | 31 | pc = Signal(5) 32 | mem = Array([Signal(5, reset=x) for x in sequence]) 33 | 34 | m.d.slow += pc.eq(Mux(pc == len(sequence), 0, pc + 1)) 35 | m.d.comb += self.leds.eq(mem[pc]) 36 | 37 | return m 38 | -------------------------------------------------------------------------------- /boards/digilent_arty_a7.py: -------------------------------------------------------------------------------- 1 | from amaranth_boards.arty_a7 import * 2 | from amaranth.build import * 3 | 4 | from top import Top 5 | 6 | if __name__ == "__main__": 7 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 8 | gpio = ("gpio", 0) 9 | platform.add_resources([ 10 | Resource("uart", 1, 11 | Subsignal("tx", Pins("1", conn=gpio, dir='o')), 12 | Subsignal("rx", Pins("2", conn=gpio, dir='i')), 13 | Attrs(IOSTANDARD="LVCMOS33") 14 | ) 15 | ]) 16 | 17 | # The platform allows access to the various resources defined by the board 18 | # definition from amaranth-boards. 19 | led0 = platform.request('led', 0) 20 | led1 = platform.request('led', 1) 21 | led2 = platform.request('led', 2) 22 | led3 = platform.request('led', 3) 23 | rgb = platform.request('rgb_led') 24 | leds = [led0, led1, led2, led3, rgb.r] 25 | uart = platform.request('uart', 1) 26 | 27 | platform.build(Top(leds, uart), do_program=True) 28 | 29 | -------------------------------------------------------------------------------- /11_modules/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Memory(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | ADD x1, x0, x0 11 | ADDI x2, x0, 31 12 | l0: 13 | ADDI x1, x1, 1 14 | BNE x1, x2, l0 15 | EBREAK 16 | """) 17 | 18 | a.assemble() 19 | self.instructions = a.mem 20 | print("memory = {}".format(self.instructions)) 21 | 22 | # Instruction memory initialised with above instructions 23 | self.mem = Array([Signal(32, reset=x, name="mem") 24 | for x in self.instructions]) 25 | 26 | self.mem_addr = Signal(32) 27 | self.mem_rdata = Signal(32) 28 | self.mem_rstrb = Signal() 29 | 30 | def elaborate(self, platform): 31 | m = Module() 32 | 33 | with m.If(self.mem_rstrb): 34 | m.d.sync += self.mem_rdata.eq(self.mem[self.mem_addr[2:32]]) 35 | 36 | return m 37 | -------------------------------------------------------------------------------- /12_size_optimisation/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Memory(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | ADD x1, x0, x0 11 | ADDI x2, x0, 31 12 | l0: 13 | ADDI x1, x1, 1 14 | BNE x1, x2, l0 15 | EBREAK 16 | """) 17 | 18 | a.assemble() 19 | self.instructions = a.mem 20 | print("memory = {}".format(self.instructions)) 21 | 22 | # Instruction memory initialised with above instructions 23 | self.mem = Array([Signal(32, reset=x, name="mem") 24 | for x in self.instructions]) 25 | 26 | self.mem_addr = Signal(32) 27 | self.mem_rdata = Signal(32) 28 | self.mem_rstrb = Signal() 29 | 30 | def elaborate(self, platform): 31 | m = Module() 32 | 33 | with m.If(self.mem_rstrb): 34 | m.d.sync += self.mem_rdata.eq(self.mem[self.mem_addr[2:32]]) 35 | 36 | return m 37 | -------------------------------------------------------------------------------- /02_slower_blinky/soc.py: -------------------------------------------------------------------------------- 1 | from amaranth import Signal, Module 2 | from amaranth.lib import wiring 3 | from amaranth.lib.wiring import In, Out 4 | 5 | # Import the new Clockworks class that handles creating new clock signals 6 | from clockworks import Clockworks 7 | 8 | class SOC(wiring.Component): 9 | 10 | leds: Out(5) 11 | 12 | def __init__(self): 13 | super().__init__() 14 | 15 | def elaborate(self, platform): 16 | 17 | m = Module() 18 | 19 | count = Signal(5) 20 | 21 | # Instantiate the clockwork with a divider of 2^21 22 | cw = Clockworks(m, slow=21) 23 | 24 | # Add the clockwork to the top module. If this is not done, 25 | # the logic will not be instantiated. 26 | m.submodules.cw = cw 27 | 28 | # The clockwork provides a new clock domain called 'slow'. 29 | # We replace the default sync domain with the new one to have the 30 | # counter run slower. 31 | m.d.slow += count.eq(count + 1) 32 | 33 | m.d.comb += self.leds.eq(count) 34 | 35 | return m 36 | -------------------------------------------------------------------------------- /tests/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | from ctypes import c_int32 as int32 4 | 5 | from soc import SOC 6 | 7 | soc = SOC() 8 | 9 | sim = Simulator(soc) 10 | 11 | def proc(): 12 | cpu = soc.cpu 13 | mem = soc.memory 14 | prev_pc = 0 15 | while True: 16 | pc = yield cpu.pc 17 | uart_valid = (yield soc.uart_valid) 18 | if uart_valid: 19 | print("out: '{}'".format(chr((yield soc.mem_wdata[0:8])))) 20 | rdata = (yield mem.mem_rdata) 21 | if prev_pc != pc: 22 | prev_pc = pc 23 | if rdata == 0b00000000000000000000000000110011: 24 | print("pc=0x{:04x}={:4d} NOP!".format(pc, pc)) 25 | for i in range(5): 26 | reg = (yield cpu.regs[10 + i]) 27 | regi = int32(reg).value 28 | print(" a{} = {}={}".format(i, regi, hex(reg))) 29 | 30 | yield 31 | 32 | sim.add_clock(1e-6) 33 | sim.add_sync_process(proc) 34 | 35 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 36 | # Let's run for a quite long time 37 | sim.run_until(2, ) 38 | -------------------------------------------------------------------------------- /01_blink/soc.py: -------------------------------------------------------------------------------- 1 | from amaranth import Signal, Module 2 | from amaranth.lib import wiring 3 | from amaranth.lib.wiring import In, Out 4 | 5 | # Any wiring.Component class is used to generate HDL output 6 | class SOC(wiring.Component): 7 | 8 | # Inputs and outputs are Signals that constitute the interface of a 9 | # Component. They are usually created with their number of bits 10 | # (default = 1). 11 | # Signals declared as In or Out are accessible via self. 12 | # from inside the class. 13 | leds: Out(5) 14 | 15 | def __init__(self): 16 | # Call the parent constructor 17 | super().__init__() 18 | 19 | def elaborate(self, platform): 20 | 21 | # Create a new Amaranth module 22 | m = Module() 23 | 24 | # This is a local signal, which is not accessible from outside. 25 | count = Signal(5) 26 | 27 | # In the sync domain all logic is clocked at the positive edge of 28 | # the implicit clk signal. 29 | m.d.sync += count.eq(count + 1) 30 | 31 | # The comb domain contains logic that is unclocked and purely 32 | # combinational. 33 | m.d.comb += self.leds.eq(count) 34 | 35 | return m 36 | -------------------------------------------------------------------------------- /14_subroutines_v2/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Memory(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | LI a0, 0 11 | 12 | l0: 13 | ADDI a0, a0, 1 14 | CALL wait 15 | J l0 16 | EBREAK 17 | 18 | wait: 19 | LI a1, 1 20 | SLLI a1, a1, 20 21 | 22 | l1: 23 | ADDI a1, a1, -1 24 | BNEZ a1, l1 25 | RET 26 | """) 27 | 28 | a.assemble() 29 | self.instructions = a.mem 30 | print("memory = {}".format(self.instructions)) 31 | 32 | # Instruction memory initialised with above instructions 33 | self.mem = Array([Signal(32, reset=x, name="mem") 34 | for x in self.instructions]) 35 | 36 | self.mem_addr = Signal(32) 37 | self.mem_rdata = Signal(32) 38 | self.mem_rstrb = Signal() 39 | 40 | def elaborate(self, platform): 41 | m = Module() 42 | 43 | with m.If(self.mem_rstrb): 44 | m.d.sync += self.mem_rdata.eq(self.mem[self.mem_addr[2:32]]) 45 | 46 | return m 47 | -------------------------------------------------------------------------------- /13_subroutines/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Memory(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | ADD x10, x0, x0 11 | 12 | l0: 13 | ADDI x10, x10, 1 14 | JAL x1, wait 15 | JAL zero, l0 16 | EBREAK 17 | 18 | wait: 19 | ADDI x11, x0, 1 20 | SLLI x11, x11, 20 21 | 22 | l1: 23 | ADDI x11, x11, -1 24 | BNE x11, x0, l1 25 | JALR x0, x1, 0 26 | """) 27 | 28 | a.assemble() 29 | self.instructions = a.mem 30 | print("memory = {}".format(self.instructions)) 31 | 32 | # Instruction memory initialised with above instructions 33 | self.mem = Array([Signal(32, reset=x, name="mem") 34 | for x in self.instructions]) 35 | 36 | self.mem_addr = Signal(32) 37 | self.mem_rdata = Signal(32) 38 | self.mem_rstrb = Signal() 39 | 40 | def elaborate(self, platform): 41 | m = Module() 42 | 43 | with m.If(self.mem_rstrb): 44 | m.d.sync += self.mem_rdata.eq(self.mem[self.mem_addr[2:32]]) 45 | 46 | return m 47 | -------------------------------------------------------------------------------- /06_alu/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /15_load/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /tests/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /07_assembler/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /08_jumps/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /09_branches/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /10_lui_auipc/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /11_modules/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /16_store/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /03_blink_from_rom/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /05_register_bank/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /13_subroutines/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /14_subroutines_v2/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /17_memory_map/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /18_mandelbrot/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /04_instruction_decoder/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /12_size_optimisation/blink.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth_boards.arty_a7 import * 3 | 4 | from soc import SOC 5 | 6 | # A platform contains board specific information about FPGA pin assignments, 7 | # toolchain and specific information for uploading the bitfile. 8 | platform = ArtyA7_35Platform(toolchain="Symbiflow") 9 | 10 | # We need a top level module 11 | m = Module() 12 | 13 | # This is the instance of our SOC 14 | soc = SOC() 15 | 16 | # The SOC is turned into a submodule (fragment) of our top level module. 17 | m.submodules.soc = soc 18 | 19 | # The platform allows access to the various resources defined by the board 20 | # definition from amaranth-boards. 21 | led0 = platform.request('led', 0) 22 | led1 = platform.request('led', 1) 23 | led2 = platform.request('led', 2) 24 | led3 = platform.request('led', 3) 25 | rgb = platform.request('rgb_led') 26 | 27 | # We connect the SOC leds signal to the various LEDs on the board. 28 | m.d.comb += [ 29 | led0.o.eq(soc.leds[0]), 30 | led1.o.eq(soc.leds[1]), 31 | led1.o.eq(soc.leds[2]), 32 | led1.o.eq(soc.leds[3]), 33 | rgb.r.o.eq(soc.leds[4]), 34 | ] 35 | 36 | # To generate the bitstream, we build() the platform using our top level 37 | # module m. 38 | platform.build(m, do_program=False) 39 | -------------------------------------------------------------------------------- /04_instruction_decoder/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_pc = 0 11 | 12 | def proc(): 13 | while True: 14 | global prev_pc 15 | pc = yield soc.pc 16 | if prev_pc != pc: 17 | print("pc={}".format(pc)) 18 | print("instr={:#032b}".format((yield soc.instr))) 19 | print("LEDS = {:05b}".format((yield soc.leds))) 20 | if (yield soc.isALUreg): 21 | print("ALUreg rd={} rs1={} rs2={} funct3={}".format( 22 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.rs2Id), 23 | (yield soc.funct3))) 24 | if (yield soc.isALUimm): 25 | print("ALUimm rd={} rs1={} imm={} funct3={}".format( 26 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.Iimm), 27 | (yield soc.funct3))) 28 | if (yield soc.isLoad): 29 | print("LOAD") 30 | if (yield soc.isStore): 31 | print("STORE") 32 | if (yield soc.isSystem): 33 | print("SYSTEM") 34 | break 35 | yield 36 | prev_pc = pc 37 | 38 | sim.add_clock(1e-6) 39 | sim.add_sync_process(proc) 40 | 41 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 42 | # Let's run for a quite long time 43 | sim.run_until(2, ) 44 | -------------------------------------------------------------------------------- /05_register_bank/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | prev_clk = 0 9 | 10 | async def testbench(ctx): 11 | while True: 12 | global prev_clk 13 | clk = ctx.get(soc.slow_clk) 14 | if prev_clk == 0 and prev_clk != clk: 15 | print("pc={}".format(ctx.get(soc.pc))) 16 | print("instr={:#032b}".format(ctx.get(soc.instr))) 17 | print("LEDS = {:05b}".format(ctx.get(soc.leds))) 18 | if ctx.get(soc.isALUreg): 19 | print("ALUreg rd={} rs1={} rs2={} funct3={}".format( 20 | ctx.get(soc.rdId), ctx.get(soc.rs1Id), ctx.get(soc.rs2Id), 21 | ctx.get(soc.funct3))) 22 | if ctx.get(soc.isALUimm): 23 | print("ALUimm rd={} rs1={} imm={} funct3={}".format( 24 | ctx.get(soc.rdId), ctx.get(soc.rs1Id), ctx.get(soc.Iimm), 25 | ctx.get(soc.funct3))) 26 | if ctx.get(soc.isLoad): 27 | print("LOAD") 28 | if ctx.get(soc.isStore): 29 | print("STORE") 30 | if ctx.get(soc.isSystem): 31 | print("SYSTEM") 32 | break 33 | await ctx.tick() 34 | prev_clk = clk 35 | 36 | sim = Simulator(soc) 37 | sim.add_clock(1e-6) 38 | sim.add_testbench(testbench) 39 | 40 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 41 | # Let's run for a quite long time 42 | sim.run_until(2, ) 43 | -------------------------------------------------------------------------------- /15_load/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Memory(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | LI s0, 0 11 | LI s1, 16 12 | 13 | l0: 14 | LB a0, s0, 400 15 | CALL wait 16 | ADDI s0, s0, 1 17 | BNE s0, s1, l0 18 | EBREAK 19 | 20 | wait: 21 | LI t0, 1 22 | SLLI t0, t0, 20 23 | 24 | l1: 25 | ADDI t0, t0, -1 26 | BNEZ t0, l1 27 | RET 28 | """) 29 | 30 | a.assemble() 31 | self.instructions = a.mem 32 | print("memory = {}".format(self.instructions)) 33 | 34 | # Instruction memory initialised with above instructions 35 | self.mem = Array([Signal(32, reset=x, name="mem{}".format(i)) 36 | for i,x in enumerate(self.instructions)]) 37 | 38 | self.mem_addr = Signal(32) 39 | self.mem_rdata = Signal(32) 40 | self.mem_rstrb = Signal() 41 | 42 | while(len(self.mem) < 100): 43 | self.mem.append(0) 44 | 45 | self.mem.append(0x04030201) 46 | self.mem.append(0x08070605) 47 | self.mem.append(0x0c0b0a09) 48 | self.mem.append(0xff0f0e0d) 49 | 50 | print(self.mem) 51 | 52 | def elaborate(self, platform): 53 | m = Module() 54 | 55 | with m.If(self.mem_rstrb): 56 | m.d.sync += self.mem_rdata.eq(self.mem[self.mem_addr[2:32]]) 57 | 58 | return m 59 | -------------------------------------------------------------------------------- /LICENSES/BSD-3-Clause: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Bastian Löher 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /18_mandelbrot/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | from ctypes import c_int32 as int32 4 | 5 | from soc import SOC 6 | 7 | soc = SOC() 8 | 9 | sim = Simulator(soc) 10 | 11 | def proc(): 12 | cpu = soc.cpu 13 | mem = soc.memory 14 | prev_pc = 0 15 | n_nops = 0 16 | while True: 17 | pc = yield cpu.pc 18 | uart_valid = (yield soc.uart_valid) 19 | if uart_valid: 20 | print("out: '{}'".format(chr((yield soc.mem_wdata[0:8])))) 21 | rdata = (yield mem.mem_rdata) 22 | if prev_pc != pc: 23 | prev_pc = pc 24 | if rdata == 0b00000000000000000000000000110011: 25 | print("NOP {:03d}: pc=0x{:04x}={:4d}".format(n_nops, pc, pc)) 26 | n_nops += 1 27 | for i in range(5): 28 | reg = (yield cpu.regs[10 + i]) 29 | regi = int32(reg).value 30 | print(" a{} = {}={}".format(i, regi, hex(reg))) 31 | for i in range(2): 32 | reg = (yield cpu.regs[8 + i]) 33 | regi = int32(reg).value 34 | print(" s{} = {}={}".format(i, regi, hex(reg))) 35 | for i in range(0,10): 36 | reg = (yield cpu.regs[18 + i]) 37 | regi = int32(reg).value 38 | print(" s{} = {}={}".format(i+2, regi, hex(reg))) 39 | 40 | yield 41 | 42 | sim.add_clock(1e-6) 43 | sim.add_sync_process(proc) 44 | 45 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 46 | # Let's run for a quite long time 47 | sim.run_until(2, ) 48 | -------------------------------------------------------------------------------- /tools/elf2hex.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | filename = sys.argv[1] 4 | 5 | class Ram(): 6 | def __init__(self): 7 | self.max_addr = 0 8 | self.mem = 0 9 | 10 | class Elf32Header(): 11 | def __init__(self, data): 12 | def get(data): 13 | return int.from_bytes(data, byteorder='little') 14 | self.ident = data[0:16] 15 | self.type = get(data[16:18]) 16 | self.machine = get(data[18:20]) 17 | self.version = get(data[20:24]) 18 | self.entry = get(data[24:28]) 19 | self.phoff = get(data[28:32]) 20 | self.shoff = get(data[32:36]) 21 | self.flags = get(data[36:40]) 22 | self.ehsize = get(data[40:42]) 23 | self.phentsize = get(data[42:44]) 24 | self.phnum = get(data[44:46]) 25 | self.shentsize = get(data[46:48]) 26 | self.shnum = get(data[48:50]) 27 | self.shstrndx = get(data[50:52]) 28 | 29 | if self.ehsize != 52: 30 | print("I'll fix it.") 31 | 32 | def __str__(self): 33 | text = "" 34 | text += "type= 0x{:04x}\n".format(self.type) 35 | text += "machine= 0x{:04x}\n".format(self.machine) 36 | text += "version= 0x{:04x}\n".format(self.version) 37 | text += "ehsize= 0x{:04x}\n".format(self.ehsize) 38 | text += "shentsize=0x{:04x}\n".format(self.shentsize) 39 | text += "shnum= 0x{:04x}\n".format(self.shnum) 40 | return text 41 | 42 | def load_ram_elf(filename): 43 | data = None 44 | with open(filename, "rb") as f: 45 | data = bytearray(f.read()) 46 | ram = Ram() 47 | 48 | header = Elf32Header(data) 49 | 50 | print(str(header)) 51 | 52 | return ram 53 | 54 | 55 | ram = load_ram_elf(filename) 56 | -------------------------------------------------------------------------------- /lib/uart_tx.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | # This module is designed after corescore_emitter_uart by Olof Kindgren which 4 | # is part of the corescore repository on github. 5 | # 6 | # https://github.com/olofk/corescore/blob/master/rtl/corescore_emitter_uart.v 7 | # 8 | # The original code is licensed under the Apache-2.0 license. 9 | # A copy of this license file is included in this repository in the LICENSES 10 | # directory. 11 | 12 | class UartTx(Elaboratable): 13 | 14 | def __init__(self, freq_hz=0, baud_rate=57600): 15 | self.freq_hz = freq_hz 16 | self.baud_rate = baud_rate 17 | 18 | # Inputs 19 | self.data = Signal(8) 20 | self.valid = Signal() 21 | 22 | # Outputs 23 | self.ready = Signal() 24 | self.tx = Signal() 25 | 26 | def elaborate(self, platform): 27 | 28 | start_value = self.freq_hz // self.baud_rate 29 | width = len(Const(start_value)) 30 | 31 | print("UartTx: start_value = {}, width = {}".format(start_value, width)) 32 | 33 | cnt = Signal(width+1) 34 | data = Signal(10) 35 | 36 | ready = self.ready 37 | valid = self.valid 38 | 39 | m = Module() 40 | 41 | m.d.comb += self.tx.eq(data[0] | ~(data.any())) 42 | 43 | with m.If(cnt[width] & ~(data.any())): 44 | m.d.sync += ready.eq(1) 45 | with m.Elif(valid & ready): 46 | m.d.sync += ready.eq(0) 47 | 48 | with m.If(ready | cnt[width]): 49 | # Cat needed ? 50 | m.d.sync += cnt.eq(start_value) 51 | with m.Else(): 52 | m.d.sync += cnt.eq(cnt - 1) 53 | 54 | with m.If(cnt[width]): 55 | m.d.sync += data.eq(Cat(data[1:10], C(0, 1))) 56 | with m.Elif(valid & ready): 57 | m.d.sync += data.eq(Cat(C(0, 1), self.data, C(1, 1))) 58 | 59 | return m 60 | -------------------------------------------------------------------------------- /tests/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Mem(Elaboratable): 5 | 6 | def __init__(self, simulation = False): 7 | self.simulation = simulation 8 | 9 | a = RiscvAssembler(simulation=simulation) 10 | a.read(a.testCode()) 11 | a.assemble() 12 | 13 | self.instructions = a.mem 14 | 15 | print("memory = {}".format(self.instructions)) 16 | 17 | # Instruction memory initialised with above instructions 18 | self.mem = Memory(width=32, depth=len(self.instructions), 19 | init=self.instructions, name="mem") 20 | 21 | self.mem_addr = Signal(32) 22 | self.mem_rdata = Signal(32) 23 | self.mem_rstrb = Signal() 24 | self.mem_wdata = Signal(32) 25 | self.mem_wmask = Signal(4) 26 | 27 | def elaborate(self, platform): 28 | m = Module() 29 | 30 | # Using the memory module from amaranth library, 31 | # we can use write_port and read_port to easily instantiate 32 | # platform specific primitives to access memory efficiently. 33 | w_port = m.submodules.w_port = self.mem.write_port( 34 | domain="sync", granularity=8 35 | ) 36 | r_port = m.submodules.r_port = self.mem.read_port( 37 | domain="sync", transparent=False 38 | ) 39 | 40 | word_addr = self.mem_addr[2:32] 41 | 42 | # Hook up read port 43 | m.d.comb += [ 44 | r_port.addr.eq(word_addr), 45 | r_port.en.eq(self.mem_rstrb), 46 | self.mem_rdata.eq(r_port.data) 47 | ] 48 | 49 | # Hook up write port 50 | m.d.comb += [ 51 | w_port.addr.eq(word_addr), 52 | w_port.en.eq(self.mem_wmask), 53 | w_port.data.eq(self.mem_wdata) 54 | ] 55 | 56 | return m 57 | -------------------------------------------------------------------------------- /lib/clockworks.py: -------------------------------------------------------------------------------- 1 | from amaranth import Signal, Module, ClockDomain, ClockSignal 2 | from amaranth.lib import wiring 3 | from amaranth.lib.wiring import In, Out 4 | 5 | # This module handles clock division and provides a new 'slow' clock domain 6 | 7 | clockworks_domain_name = "slow" 8 | 9 | class Clockworks(wiring.Component): 10 | 11 | o_slow: Out(1) 12 | 13 | def __init__(self, module, slow=0, sim_slow=None): 14 | 15 | # Since amaranth 0.6 clock domains do not propagate upwards (RFC59) 16 | module.domains += ClockDomain(clockworks_domain_name) 17 | 18 | # Since the module provides a new clock domain, which is accessible 19 | # via the top level module, we don't need to explicitly provide the 20 | # slow clock signal as an output. 21 | 22 | self.slow = slow 23 | if sim_slow is None: 24 | self.sim_slow = slow 25 | else: 26 | self.sim_slow = sim_slow 27 | 28 | super().__init__() 29 | 30 | def elaborate(self, platform): 31 | 32 | o_clk = Signal() 33 | m = Module() 34 | 35 | if self.slow != 0: 36 | # When the design is simulated, platform is None 37 | if platform is None: 38 | # Have the simulation run at a different speed than the 39 | # actual hardware (usually faster). 40 | slow_bit = self.sim_slow 41 | else: 42 | slow_bit = self.slow 43 | 44 | slow_clk = Signal(slow_bit + 1) 45 | m.d.sync += slow_clk.eq(slow_clk + 1) 46 | m.d.comb += o_clk.eq(slow_clk[slow_bit]) 47 | 48 | else: 49 | # When no division is requested, just use the clock signal of 50 | # the default 'sync' domain. 51 | m.d.comb += o_clk.eq(ClockSignal("sync")) 52 | 53 | # Create the new clock domain 54 | m.domains += ClockDomain("slow") 55 | 56 | # Assign the slow clock to the clock signal of the new domain 57 | m.d.comb += ClockSignal("slow").eq(o_clk) 58 | 59 | return m 60 | -------------------------------------------------------------------------------- /06_alu/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | while True: 14 | global prev_clk 15 | clk = yield soc.slow_clk 16 | if prev_clk == 0 and prev_clk != clk: 17 | state = (yield soc.state) 18 | if state == 2: 19 | print("-- NEW CYCLE -----------------------") 20 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 21 | print(" F: pc={}".format((yield soc.pc))) 22 | print(" F: instr={:#032b}".format((yield soc.instr))) 23 | if (yield soc.isALUreg): 24 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 25 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.rs2Id), 26 | (yield soc.funct3))) 27 | if (yield soc.isALUimm): 28 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 29 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.Iimm), 30 | (yield soc.funct3))) 31 | if (yield soc.isLoad): 32 | print(" LOAD") 33 | if (yield soc.isStore): 34 | print(" STORE") 35 | if (yield soc.isSystem): 36 | print(" SYSTEM") 37 | break 38 | if state == 4: 39 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 40 | print(" R: rs1={}".format((yield soc.rs1))) 41 | print(" R: rs2={}".format((yield soc.rs2))) 42 | if state == 1: 43 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 44 | print(" E: Writeback x{} = {:032b}".format((yield soc.rdId), 45 | (yield soc.writeBackData))) 46 | yield 47 | prev_clk = clk 48 | 49 | sim.add_clock(1e-6) 50 | sim.add_sync_process(proc) 51 | 52 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 53 | # Let's run for a quite long time 54 | sim.run_until(2, ) 55 | -------------------------------------------------------------------------------- /08_jumps/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | while True: 14 | global prev_clk 15 | clk = yield soc.slow_clk 16 | if prev_clk == 0 and prev_clk != clk: 17 | state = (yield soc.state) 18 | if state == 2: 19 | print("-- NEW CYCLE -----------------------") 20 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 21 | print(" F: pc={}".format((yield soc.pc))) 22 | print(" F: instr={:#032b}".format((yield soc.instr))) 23 | if (yield soc.isALUreg): 24 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 25 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.rs2Id), 26 | (yield soc.funct3))) 27 | if (yield soc.isALUimm): 28 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 29 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.Iimm), 30 | (yield soc.funct3))) 31 | if (yield soc.isLoad): 32 | print(" LOAD") 33 | if (yield soc.isStore): 34 | print(" STORE") 35 | if (yield soc.isSystem): 36 | print(" SYSTEM") 37 | break 38 | if state == 4: 39 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 40 | print(" R: rs1={}".format((yield soc.rs1))) 41 | print(" R: rs2={}".format((yield soc.rs2))) 42 | if state == 1: 43 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 44 | print(" E: Writeback x{} = {:032b}".format((yield soc.rdId), 45 | (yield soc.writeBackData))) 46 | yield 47 | prev_clk = clk 48 | 49 | sim.add_clock(1e-6) 50 | sim.add_sync_process(proc) 51 | 52 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 53 | # Let's run for a quite long time 54 | sim.run_until(2, ) 55 | -------------------------------------------------------------------------------- /07_assembler/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | while True: 14 | global prev_clk 15 | clk = yield soc.slow_clk 16 | if prev_clk == 0 and prev_clk != clk: 17 | state = (yield soc.state) 18 | if state == 2: 19 | print("-- NEW CYCLE -----------------------") 20 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 21 | print(" F: pc={}".format((yield soc.pc))) 22 | print(" F: instr={:#032b}".format((yield soc.instr))) 23 | if (yield soc.isALUreg): 24 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 25 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.rs2Id), 26 | (yield soc.funct3))) 27 | if (yield soc.isALUimm): 28 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 29 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.Iimm), 30 | (yield soc.funct3))) 31 | if (yield soc.isLoad): 32 | print(" LOAD") 33 | if (yield soc.isStore): 34 | print(" STORE") 35 | if (yield soc.isSystem): 36 | print(" SYSTEM") 37 | break 38 | if state == 4: 39 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 40 | print(" R: rs1={}".format((yield soc.rs1))) 41 | print(" R: rs2={}".format((yield soc.rs2))) 42 | if state == 1: 43 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 44 | print(" E: Writeback x{} = {:032b}".format((yield soc.rdId), 45 | (yield soc.writeBackData))) 46 | yield 47 | prev_clk = clk 48 | 49 | sim.add_clock(1e-6) 50 | sim.add_sync_process(proc) 51 | 52 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 53 | # Let's run for a quite long time 54 | sim.run_until(2, ) 55 | -------------------------------------------------------------------------------- /09_branches/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | while True: 14 | global prev_clk 15 | clk = yield soc.slow_clk 16 | if prev_clk == 0 and prev_clk != clk: 17 | state = (yield soc.state) 18 | if state == 2: 19 | print("-- NEW CYCLE -----------------------") 20 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 21 | print(" F: pc={}".format((yield soc.pc))) 22 | print(" F: instr={:#032b}".format((yield soc.instr))) 23 | if (yield soc.isALUreg): 24 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 25 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.rs2Id), 26 | (yield soc.funct3))) 27 | if (yield soc.isALUimm): 28 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 29 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.Iimm), 30 | (yield soc.funct3))) 31 | if (yield soc.isBranch): 32 | print(" BRANCH rs1={} rs2={}".format( 33 | (yield soc.rs1Id), (yield soc.rs2Id))) 34 | if (yield soc.isLoad): 35 | print(" LOAD") 36 | if (yield soc.isStore): 37 | print(" STORE") 38 | if (yield soc.isSystem): 39 | print(" SYSTEM") 40 | break 41 | if state == 4: 42 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 43 | print(" R: rs1={}".format((yield soc.rs1))) 44 | print(" R: rs2={}".format((yield soc.rs2))) 45 | if state == 1: 46 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 47 | print(" E: Writeback x{} = {:032b}".format((yield soc.rdId), 48 | (yield soc.writeBackData))) 49 | yield 50 | prev_clk = clk 51 | 52 | sim.add_clock(1e-6) 53 | sim.add_sync_process(proc) 54 | 55 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 56 | # Let's run for a quite long time 57 | sim.run_until(2, ) 58 | -------------------------------------------------------------------------------- /10_lui_auipc/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | while True: 14 | global prev_clk 15 | clk = yield soc.slow_clk 16 | if prev_clk == 0 and prev_clk != clk: 17 | state = (yield soc.state) 18 | if state == 2: 19 | print("-- NEW CYCLE -----------------------") 20 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 21 | print(" F: pc={}".format((yield soc.pc))) 22 | print(" F: instr={:#032b}".format((yield soc.instr))) 23 | if (yield soc.isALUreg): 24 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 25 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.rs2Id), 26 | (yield soc.funct3))) 27 | if (yield soc.isALUimm): 28 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 29 | (yield soc.rdId), (yield soc.rs1Id), (yield soc.Iimm), 30 | (yield soc.funct3))) 31 | if (yield soc.isBranch): 32 | print(" BRANCH rs1={} rs2={}".format( 33 | (yield soc.rs1Id), (yield soc.rs2Id))) 34 | if (yield soc.isLoad): 35 | print(" LOAD") 36 | if (yield soc.isStore): 37 | print(" STORE") 38 | if (yield soc.isSystem): 39 | print(" SYSTEM") 40 | break 41 | if state == 4: 42 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 43 | print(" R: rs1={}".format((yield soc.rs1))) 44 | print(" R: rs2={}".format((yield soc.rs2))) 45 | if state == 1: 46 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 47 | print(" E: Writeback x{} = {:032b}".format((yield soc.rdId), 48 | (yield soc.writeBackData))) 49 | yield 50 | prev_clk = clk 51 | 52 | sim.add_clock(1e-6) 53 | sim.add_sync_process(proc) 54 | 55 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 56 | # Let's run for a quite long time 57 | sim.run_until(2, ) 58 | -------------------------------------------------------------------------------- /15_load/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | cpu = soc.cpu 14 | mem = soc.memory 15 | while True: 16 | global prev_clk 17 | clk = yield soc.slow_clk 18 | if prev_clk == 0 and prev_clk != clk: 19 | state = (yield soc.cpu.fsm.state) 20 | if state == 2: 21 | print("-- NEW CYCLE -----------------------") 22 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 23 | print(" F: pc={}".format((yield cpu.pc))) 24 | print(" F: instr={:#032b}".format((yield cpu.instr))) 25 | if (yield cpu.isALUreg): 26 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 27 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.rs2Id), 28 | (yield cpu.funct3))) 29 | if (yield cpu.isALUimm): 30 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 31 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.Iimm), 32 | (yield cpu.funct3))) 33 | if (yield cpu.isBranch): 34 | print(" BRANCH rs1={} rs2={}".format( 35 | (yield cpu.rs1Id), (yield cpu.rs2Id))) 36 | if (yield cpu.isLoad): 37 | print(" LOAD") 38 | if (yield cpu.isStore): 39 | print(" STORE") 40 | if (yield cpu.isSystem): 41 | print(" SYSTEM") 42 | break 43 | if state == 4: 44 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 45 | print(" R: rs1={}".format((yield cpu.rs1))) 46 | print(" R: rs2={}".format((yield cpu.rs2))) 47 | if state == 1: 48 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 49 | print(" E: Writeback x{} = {:032b}".format((yield cpu.rdId), 50 | (yield cpu.writeBackData))) 51 | if state == 8: 52 | print(" NEW") 53 | yield 54 | prev_clk = clk 55 | 56 | sim.add_clock(1e-6) 57 | sim.add_sync_process(proc) 58 | 59 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 60 | # Let's run for a quite long time 61 | sim.run_until(2, ) 62 | -------------------------------------------------------------------------------- /16_store/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | cpu = soc.cpu 14 | mem = soc.memory 15 | while True: 16 | global prev_clk 17 | clk = yield soc.slow_clk 18 | if prev_clk == 0 and prev_clk != clk: 19 | state = (yield soc.cpu.fsm.state) 20 | if state == 2: 21 | print("-- NEW CYCLE -----------------------") 22 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 23 | print(" F: pc={}".format((yield cpu.pc))) 24 | print(" F: instr={:#032b}".format((yield cpu.instr))) 25 | if (yield cpu.isALUreg): 26 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 27 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.rs2Id), 28 | (yield cpu.funct3))) 29 | if (yield cpu.isALUimm): 30 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 31 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.Iimm), 32 | (yield cpu.funct3))) 33 | if (yield cpu.isBranch): 34 | print(" BRANCH rs1={} rs2={}".format( 35 | (yield cpu.rs1Id), (yield cpu.rs2Id))) 36 | if (yield cpu.isLoad): 37 | print(" LOAD") 38 | if (yield cpu.isStore): 39 | print(" STORE") 40 | if (yield cpu.isSystem): 41 | print(" SYSTEM") 42 | break 43 | if state == 4: 44 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 45 | print(" R: rs1={}".format((yield cpu.rs1))) 46 | print(" R: rs2={}".format((yield cpu.rs2))) 47 | if state == 1: 48 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 49 | print(" E: Writeback x{} = {:032b}".format((yield cpu.rdId), 50 | (yield cpu.writeBackData))) 51 | if state == 8: 52 | print(" NEW") 53 | yield 54 | prev_clk = clk 55 | 56 | sim.add_clock(1e-6) 57 | sim.add_sync_process(proc) 58 | 59 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 60 | # Let's run for a quite long time 61 | sim.run_until(2, ) 62 | -------------------------------------------------------------------------------- /11_modules/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | cpu = soc.cpu 14 | mem = soc.memory 15 | while True: 16 | global prev_clk 17 | clk = yield soc.slow_clk 18 | if prev_clk == 0 and prev_clk != clk: 19 | state = (yield soc.cpu.fsm.state) 20 | if state == 2: 21 | print("-- NEW CYCLE -----------------------") 22 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 23 | print(" F: pc={}".format((yield cpu.pc))) 24 | print(" F: instr={:#032b}".format((yield cpu.instr))) 25 | if (yield cpu.isALUreg): 26 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 27 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.rs2Id), 28 | (yield cpu.funct3))) 29 | if (yield cpu.isALUimm): 30 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 31 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.Iimm), 32 | (yield cpu.funct3))) 33 | if (yield cpu.isBranch): 34 | print(" BRANCH rs1={} rs2={}".format( 35 | (yield cpu.rs1Id), (yield cpu.rs2Id))) 36 | if (yield cpu.isLoad): 37 | print(" LOAD") 38 | if (yield cpu.isStore): 39 | print(" STORE") 40 | if (yield cpu.isSystem): 41 | print(" SYSTEM") 42 | break 43 | if state == 4: 44 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 45 | print(" R: rs1={}".format((yield cpu.rs1))) 46 | print(" R: rs2={}".format((yield cpu.rs2))) 47 | if state == 1: 48 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 49 | print(" E: Writeback x{} = {:032b}".format((yield cpu.rdId), 50 | (yield cpu.writeBackData))) 51 | if state == 8: 52 | print(" NEW") 53 | yield 54 | prev_clk = clk 55 | 56 | sim.add_clock(1e-6) 57 | sim.add_sync_process(proc) 58 | 59 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 60 | # Let's run for a quite long time 61 | sim.run_until(2, ) 62 | -------------------------------------------------------------------------------- /17_memory_map/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | cpu = soc.cpu 14 | mem = soc.memory 15 | while True: 16 | global prev_clk 17 | clk = yield soc.slow_clk 18 | if prev_clk == 0 and prev_clk != clk: 19 | state = (yield soc.cpu.fsm.state) 20 | if state == 2: 21 | print("-- NEW CYCLE -----------------------") 22 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 23 | print(" F: pc={}".format((yield cpu.pc))) 24 | print(" F: instr={:#032b}".format((yield cpu.instr))) 25 | if (yield cpu.isALUreg): 26 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 27 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.rs2Id), 28 | (yield cpu.funct3))) 29 | if (yield cpu.isALUimm): 30 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 31 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.Iimm), 32 | (yield cpu.funct3))) 33 | if (yield cpu.isBranch): 34 | print(" BRANCH rs1={} rs2={}".format( 35 | (yield cpu.rs1Id), (yield cpu.rs2Id))) 36 | if (yield cpu.isLoad): 37 | print(" LOAD") 38 | if (yield cpu.isStore): 39 | print(" STORE") 40 | if (yield cpu.isSystem): 41 | print(" SYSTEM") 42 | break 43 | if state == 4: 44 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 45 | print(" R: rs1={}".format((yield cpu.rs1))) 46 | print(" R: rs2={}".format((yield cpu.rs2))) 47 | if state == 1: 48 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 49 | print(" E: Writeback x{} = {:032b}".format((yield cpu.rdId), 50 | (yield cpu.writeBackData))) 51 | if state == 8: 52 | print(" NEW") 53 | yield 54 | prev_clk = clk 55 | 56 | sim.add_clock(1e-6) 57 | sim.add_sync_process(proc) 58 | 59 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 60 | # Let's run for a quite long time 61 | sim.run_until(2, ) 62 | -------------------------------------------------------------------------------- /13_subroutines/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | cpu = soc.cpu 14 | mem = soc.memory 15 | while True: 16 | global prev_clk 17 | clk = yield soc.slow_clk 18 | if prev_clk == 0 and prev_clk != clk: 19 | state = (yield soc.cpu.fsm.state) 20 | if state == 2: 21 | print("-- NEW CYCLE -----------------------") 22 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 23 | print(" F: pc={}".format((yield cpu.pc))) 24 | print(" F: instr={:#032b}".format((yield cpu.instr))) 25 | if (yield cpu.isALUreg): 26 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 27 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.rs2Id), 28 | (yield cpu.funct3))) 29 | if (yield cpu.isALUimm): 30 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 31 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.Iimm), 32 | (yield cpu.funct3))) 33 | if (yield cpu.isBranch): 34 | print(" BRANCH rs1={} rs2={}".format( 35 | (yield cpu.rs1Id), (yield cpu.rs2Id))) 36 | if (yield cpu.isLoad): 37 | print(" LOAD") 38 | if (yield cpu.isStore): 39 | print(" STORE") 40 | if (yield cpu.isSystem): 41 | print(" SYSTEM") 42 | break 43 | if state == 4: 44 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 45 | print(" R: rs1={}".format((yield cpu.rs1))) 46 | print(" R: rs2={}".format((yield cpu.rs2))) 47 | if state == 1: 48 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 49 | print(" E: Writeback x{} = {:032b}".format((yield cpu.rdId), 50 | (yield cpu.writeBackData))) 51 | if state == 8: 52 | print(" NEW") 53 | yield 54 | prev_clk = clk 55 | 56 | sim.add_clock(1e-6) 57 | sim.add_sync_process(proc) 58 | 59 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 60 | # Let's run for a quite long time 61 | sim.run_until(2, ) 62 | -------------------------------------------------------------------------------- /14_subroutines_v2/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | cpu = soc.cpu 14 | mem = soc.memory 15 | while True: 16 | global prev_clk 17 | clk = yield soc.slow_clk 18 | if prev_clk == 0 and prev_clk != clk: 19 | state = (yield soc.cpu.fsm.state) 20 | if state == 2: 21 | print("-- NEW CYCLE -----------------------") 22 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 23 | print(" F: pc={}".format((yield cpu.pc))) 24 | print(" F: instr={:#032b}".format((yield cpu.instr))) 25 | if (yield cpu.isALUreg): 26 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 27 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.rs2Id), 28 | (yield cpu.funct3))) 29 | if (yield cpu.isALUimm): 30 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 31 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.Iimm), 32 | (yield cpu.funct3))) 33 | if (yield cpu.isBranch): 34 | print(" BRANCH rs1={} rs2={}".format( 35 | (yield cpu.rs1Id), (yield cpu.rs2Id))) 36 | if (yield cpu.isLoad): 37 | print(" LOAD") 38 | if (yield cpu.isStore): 39 | print(" STORE") 40 | if (yield cpu.isSystem): 41 | print(" SYSTEM") 42 | break 43 | if state == 4: 44 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 45 | print(" R: rs1={}".format((yield cpu.rs1))) 46 | print(" R: rs2={}".format((yield cpu.rs2))) 47 | if state == 1: 48 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 49 | print(" E: Writeback x{} = {:032b}".format((yield cpu.rdId), 50 | (yield cpu.writeBackData))) 51 | if state == 8: 52 | print(" NEW") 53 | yield 54 | prev_clk = clk 55 | 56 | sim.add_clock(1e-6) 57 | sim.add_sync_process(proc) 58 | 59 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 60 | # Let's run for a quite long time 61 | sim.run_until(2, ) 62 | -------------------------------------------------------------------------------- /12_size_optimisation/bench.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from amaranth.sim import * 3 | 4 | from soc import SOC 5 | 6 | soc = SOC() 7 | 8 | sim = Simulator(soc) 9 | 10 | prev_clk = 0 11 | 12 | def proc(): 13 | cpu = soc.cpu 14 | mem = soc.memory 15 | while True: 16 | global prev_clk 17 | clk = yield soc.slow_clk 18 | if prev_clk == 0 and prev_clk != clk: 19 | state = (yield soc.cpu.fsm.state) 20 | if state == 2: 21 | print("-- NEW CYCLE -----------------------") 22 | print(" F: LEDS = {:05b}".format((yield soc.leds))) 23 | print(" F: pc={}".format((yield cpu.pc))) 24 | print(" F: instr={:#032b}".format((yield cpu.instr))) 25 | if (yield cpu.isALUreg): 26 | print(" ALUreg rd={} rs1={} rs2={} funct3={}".format( 27 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.rs2Id), 28 | (yield cpu.funct3))) 29 | if (yield cpu.isALUimm): 30 | print(" ALUimm rd={} rs1={} imm={} funct3={}".format( 31 | (yield cpu.rdId), (yield cpu.rs1Id), (yield cpu.Iimm), 32 | (yield cpu.funct3))) 33 | if (yield cpu.isBranch): 34 | print(" BRANCH rs1={} rs2={}".format( 35 | (yield cpu.rs1Id), (yield cpu.rs2Id))) 36 | if (yield cpu.isLoad): 37 | print(" LOAD") 38 | if (yield cpu.isStore): 39 | print(" STORE") 40 | if (yield cpu.isSystem): 41 | print(" SYSTEM") 42 | break 43 | if state == 4: 44 | print(" R: LEDS = {:05b}".format((yield soc.leds))) 45 | print(" R: rs1={}".format((yield cpu.rs1))) 46 | print(" R: rs2={}".format((yield cpu.rs2))) 47 | if state == 1: 48 | print(" E: LEDS = {:05b}".format((yield soc.leds))) 49 | print(" E: Writeback x{} = {:032b}".format((yield cpu.rdId), 50 | (yield cpu.writeBackData))) 51 | if state == 8: 52 | print(" NEW") 53 | yield 54 | prev_clk = clk 55 | 56 | sim.add_clock(1e-6) 57 | sim.add_sync_process(proc) 58 | 59 | with sim.write_vcd('bench.vcd', 'bench.gtkw', traces=soc.ports): 60 | # Let's run for a quite long time 61 | sim.run_until(2, ) 62 | -------------------------------------------------------------------------------- /16_store/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Memory(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | LI a0, 0 11 | LI s1, 16 12 | LI s0, 0 13 | 14 | l0: 15 | LB a1, s0, 400 16 | SB a1, s0, 800 17 | CALL wait 18 | ADDI s0, s0, 1 19 | BNE s0, s1, l0 20 | 21 | LI s0, 0 22 | 23 | l1: 24 | LB a0, s0, 800 25 | CALL wait 26 | ADDI s0, s0, 1 27 | BNE s0, s1, l1 28 | EBREAK 29 | 30 | wait: 31 | LI t0, 1 32 | SLLI t0, t0, 3 33 | 34 | l2: 35 | ADDI t0, t0, -1 36 | BNEZ t0, l2 37 | RET 38 | """) 39 | 40 | a.assemble() 41 | self.instructions = a.mem 42 | 43 | # Add some data at offset 400 / word 100 44 | while len(self.instructions) < 100: 45 | self.instructions.append(0) 46 | self.instructions.append(0x04030201) 47 | self.instructions.append(0x08070605) 48 | self.instructions.append(0x0c0b0a09) 49 | self.instructions.append(0xff0f0e0d) 50 | # Add 0 memory up to offset 1024 / word 256 51 | while len(self.instructions) < 256: 52 | self.instructions.append(0) 53 | 54 | print("memory = {}".format(self.instructions)) 55 | 56 | # Instruction memory initialised with above instructions 57 | self.mem = Array([Signal(32, reset=x, name="mem{}".format(i)) 58 | for i,x in enumerate(self.instructions)]) 59 | 60 | self.mem_addr = Signal(32) 61 | self.mem_rdata = Signal(32) 62 | self.mem_rstrb = Signal() 63 | self.mem_wdata = Signal(32) 64 | self.mem_wmask = Signal(4) 65 | 66 | def elaborate(self, platform): 67 | m = Module() 68 | 69 | word_addr = self.mem_addr[2:32] 70 | 71 | with m.If(self.mem_rstrb): 72 | m.d.sync += self.mem_rdata.eq(self.mem[word_addr]) 73 | with m.If(self.mem_wmask[0]): 74 | m.d.sync += self.mem[word_addr][0:8].eq(self.mem_wdata[0:8]) 75 | with m.If(self.mem_wmask[1]): 76 | m.d.sync += self.mem[word_addr][8:16].eq(self.mem_wdata[8:16]) 77 | with m.If(self.mem_wmask[2]): 78 | m.d.sync += self.mem[word_addr][16:24].eq(self.mem_wdata[16:24]) 79 | with m.If(self.mem_wmask[3]): 80 | m.d.sync += self.mem[word_addr][24:32].eq(self.mem_wdata[24:32]) 81 | 82 | return m 83 | -------------------------------------------------------------------------------- /15_load/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Memory 6 | from cpu import CPU 7 | 8 | class SOC(Elaboratable): 9 | 10 | def __init__(self): 11 | 12 | self.leds = Signal(5) 13 | 14 | # Signals in this list can easily be plotted as vcd traces 15 | self.ports = [] 16 | 17 | def elaborate(self, platform): 18 | 19 | m = Module() 20 | cw = Clockworks(m) 21 | memory = DomainRenamer("slow")(Memory()) 22 | cpu = DomainRenamer("slow")(CPU()) 23 | m.submodules.cw = cw 24 | m.submodules.cpu = cpu 25 | m.submodules.memory = memory 26 | 27 | self.cpu = cpu 28 | self.memory = memory 29 | 30 | x10 = Signal(32) 31 | 32 | # Connect memory to CPU 33 | m.d.comb += [ 34 | memory.mem_addr.eq(cpu.mem_addr), 35 | memory.mem_rstrb.eq(cpu.mem_rstrb), 36 | cpu.mem_rdata.eq(memory.mem_rdata) 37 | ] 38 | 39 | # CPU debug output 40 | m.d.comb += [ 41 | x10.eq(cpu.x10), 42 | self.leds.eq(x10[0:5]) 43 | ] 44 | 45 | # Export signals for simulation 46 | def export(signal, name): 47 | if type(signal) is not Signal: 48 | newsig = Signal(signal.shape(), name = name) 49 | m.d.comb += newsig.eq(signal) 50 | else: 51 | newsig = signal 52 | self.ports.append(newsig) 53 | setattr(self, name, newsig) 54 | 55 | if platform is None: 56 | export(ClockSignal("slow"), "slow_clk") 57 | #export(pc, "pc") 58 | #export(instr, "instr") 59 | #export(isALUreg, "isALUreg") 60 | #export(isALUimm, "isALUimm") 61 | #export(isBranch, "isBranch") 62 | #export(isJAL, "isJAL") 63 | #export(isJALR, "isJALR") 64 | #export(isLoad, "isLoad") 65 | #export(isStore, "isStore") 66 | #export(isSystem, "isSystem") 67 | #export(rdId, "rdId") 68 | #export(rs1Id, "rs1Id") 69 | #export(rs2Id, "rs2Id") 70 | #export(Iimm, "Iimm") 71 | #export(Bimm, "Bimm") 72 | #export(Jimm, "Jimm") 73 | #export(funct3, "funct3") 74 | #export(rdId, "rdId") 75 | #export(rs1, "rs1") 76 | #export(rs2, "rs2") 77 | #export(writeBackData, "writeBackData") 78 | #export(writeBackEn, "writeBackEn") 79 | #export(aluOut, "aluOut") 80 | #export((1 << cpu.fsm.state), "state") 81 | 82 | return m 83 | -------------------------------------------------------------------------------- /13_subroutines/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Memory 6 | from cpu import CPU 7 | 8 | class SOC(Elaboratable): 9 | 10 | def __init__(self): 11 | 12 | self.leds = Signal(5) 13 | 14 | # Signals in this list can easily be plotted as vcd traces 15 | self.ports = [] 16 | 17 | def elaborate(self, platform): 18 | 19 | m = Module() 20 | cw = Clockworks(m) 21 | memory = DomainRenamer("slow")(Memory()) 22 | cpu = DomainRenamer("slow")(CPU()) 23 | m.submodules.cw = cw 24 | m.submodules.cpu = cpu 25 | m.submodules.memory = memory 26 | 27 | self.cpu = cpu 28 | self.memory = memory 29 | 30 | x10 = Signal(32) 31 | 32 | # Connect memory to CPU 33 | m.d.comb += [ 34 | memory.mem_addr.eq(cpu.mem_addr), 35 | memory.mem_rstrb.eq(cpu.mem_rstrb), 36 | cpu.mem_rdata.eq(memory.mem_rdata) 37 | ] 38 | 39 | # CPU debug output 40 | m.d.comb += [ 41 | x10.eq(cpu.x10), 42 | self.leds.eq(x10[0:5]) 43 | ] 44 | 45 | # Export signals for simulation 46 | def export(signal, name): 47 | if type(signal) is not Signal: 48 | newsig = Signal(signal.shape(), name = name) 49 | m.d.comb += newsig.eq(signal) 50 | else: 51 | newsig = signal 52 | self.ports.append(newsig) 53 | setattr(self, name, newsig) 54 | 55 | if platform is None: 56 | export(ClockSignal("slow"), "slow_clk") 57 | #export(pc, "pc") 58 | #export(instr, "instr") 59 | #export(isALUreg, "isALUreg") 60 | #export(isALUimm, "isALUimm") 61 | #export(isBranch, "isBranch") 62 | #export(isJAL, "isJAL") 63 | #export(isJALR, "isJALR") 64 | #export(isLoad, "isLoad") 65 | #export(isStore, "isStore") 66 | #export(isSystem, "isSystem") 67 | #export(rdId, "rdId") 68 | #export(rs1Id, "rs1Id") 69 | #export(rs2Id, "rs2Id") 70 | #export(Iimm, "Iimm") 71 | #export(Bimm, "Bimm") 72 | #export(Jimm, "Jimm") 73 | #export(funct3, "funct3") 74 | #export(rdId, "rdId") 75 | #export(rs1, "rs1") 76 | #export(rs2, "rs2") 77 | #export(writeBackData, "writeBackData") 78 | #export(writeBackEn, "writeBackEn") 79 | #export(aluOut, "aluOut") 80 | #export((1 << cpu.fsm.state), "state") 81 | 82 | return m 83 | -------------------------------------------------------------------------------- /14_subroutines_v2/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Memory 6 | from cpu import CPU 7 | 8 | class SOC(Elaboratable): 9 | 10 | def __init__(self): 11 | 12 | self.leds = Signal(5) 13 | 14 | # Signals in this list can easily be plotted as vcd traces 15 | self.ports = [] 16 | 17 | def elaborate(self, platform): 18 | 19 | m = Module() 20 | cw = Clockworks(m) 21 | memory = DomainRenamer("slow")(Memory()) 22 | cpu = DomainRenamer("slow")(CPU()) 23 | m.submodules.cw = cw 24 | m.submodules.cpu = cpu 25 | m.submodules.memory = memory 26 | 27 | self.cpu = cpu 28 | self.memory = memory 29 | 30 | x10 = Signal(32) 31 | 32 | # Connect memory to CPU 33 | m.d.comb += [ 34 | memory.mem_addr.eq(cpu.mem_addr), 35 | memory.mem_rstrb.eq(cpu.mem_rstrb), 36 | cpu.mem_rdata.eq(memory.mem_rdata) 37 | ] 38 | 39 | # CPU debug output 40 | m.d.comb += [ 41 | x10.eq(cpu.x10), 42 | self.leds.eq(x10[0:5]) 43 | ] 44 | 45 | # Export signals for simulation 46 | def export(signal, name): 47 | if type(signal) is not Signal: 48 | newsig = Signal(signal.shape(), name = name) 49 | m.d.comb += newsig.eq(signal) 50 | else: 51 | newsig = signal 52 | self.ports.append(newsig) 53 | setattr(self, name, newsig) 54 | 55 | if platform is None: 56 | export(ClockSignal("slow"), "slow_clk") 57 | #export(pc, "pc") 58 | #export(instr, "instr") 59 | #export(isALUreg, "isALUreg") 60 | #export(isALUimm, "isALUimm") 61 | #export(isBranch, "isBranch") 62 | #export(isJAL, "isJAL") 63 | #export(isJALR, "isJALR") 64 | #export(isLoad, "isLoad") 65 | #export(isStore, "isStore") 66 | #export(isSystem, "isSystem") 67 | #export(rdId, "rdId") 68 | #export(rs1Id, "rs1Id") 69 | #export(rs2Id, "rs2Id") 70 | #export(Iimm, "Iimm") 71 | #export(Bimm, "Bimm") 72 | #export(Jimm, "Jimm") 73 | #export(funct3, "funct3") 74 | #export(rdId, "rdId") 75 | #export(rs1, "rs1") 76 | #export(rs2, "rs2") 77 | #export(writeBackData, "writeBackData") 78 | #export(writeBackEn, "writeBackEn") 79 | #export(aluOut, "aluOut") 80 | #export((1 << cpu.fsm.state), "state") 81 | 82 | return m 83 | -------------------------------------------------------------------------------- /11_modules/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Memory 6 | from cpu import CPU 7 | 8 | class SOC(Elaboratable): 9 | 10 | def __init__(self): 11 | 12 | self.leds = Signal(5) 13 | 14 | # Signals in this list can easily be plotted as vcd traces 15 | self.ports = [] 16 | 17 | def elaborate(self, platform): 18 | 19 | m = Module() 20 | cw = Clockworks(m, slow=19, sim_slow=10) 21 | memory = DomainRenamer("slow")(Memory()) 22 | cpu = DomainRenamer("slow")(CPU()) 23 | m.submodules.cw = cw 24 | m.submodules.cpu = cpu 25 | m.submodules.memory = memory 26 | 27 | self.cpu = cpu 28 | self.memory = memory 29 | 30 | x1 = Signal(32) 31 | 32 | # Connect memory to CPU 33 | m.d.comb += [ 34 | memory.mem_addr.eq(cpu.mem_addr), 35 | memory.mem_rstrb.eq(cpu.mem_rstrb), 36 | cpu.mem_rdata.eq(memory.mem_rdata) 37 | ] 38 | 39 | # CPU debug output 40 | m.d.comb += [ 41 | x1.eq(cpu.x1), 42 | self.leds.eq(x1[0:5]) 43 | ] 44 | 45 | # Export signals for simulation 46 | def export(signal, name): 47 | if type(signal) is not Signal: 48 | newsig = Signal(signal.shape(), name = name) 49 | m.d.comb += newsig.eq(signal) 50 | else: 51 | newsig = signal 52 | self.ports.append(newsig) 53 | setattr(self, name, newsig) 54 | 55 | if platform is None: 56 | export(ClockSignal("slow"), "slow_clk") 57 | #export(pc, "pc") 58 | #export(instr, "instr") 59 | #export(isALUreg, "isALUreg") 60 | #export(isALUimm, "isALUimm") 61 | #export(isBranch, "isBranch") 62 | #export(isJAL, "isJAL") 63 | #export(isJALR, "isJALR") 64 | #export(isLoad, "isLoad") 65 | #export(isStore, "isStore") 66 | #export(isSystem, "isSystem") 67 | #export(rdId, "rdId") 68 | #export(rs1Id, "rs1Id") 69 | #export(rs2Id, "rs2Id") 70 | #export(Iimm, "Iimm") 71 | #export(Bimm, "Bimm") 72 | #export(Jimm, "Jimm") 73 | #export(funct3, "funct3") 74 | #export(rdId, "rdId") 75 | #export(rs1, "rs1") 76 | #export(rs2, "rs2") 77 | #export(writeBackData, "writeBackData") 78 | #export(writeBackEn, "writeBackEn") 79 | #export(aluOut, "aluOut") 80 | #export((1 << cpu.fsm.state), "state") 81 | 82 | return m 83 | -------------------------------------------------------------------------------- /12_size_optimisation/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Memory 6 | from cpu import CPU 7 | 8 | class SOC(Elaboratable): 9 | 10 | def __init__(self): 11 | 12 | self.leds = Signal(5) 13 | 14 | # Signals in this list can easily be plotted as vcd traces 15 | self.ports = [] 16 | 17 | def elaborate(self, platform): 18 | 19 | m = Module() 20 | cw = Clockworks(m, slow=19, sim_slow=10) 21 | memory = DomainRenamer("slow")(Memory()) 22 | cpu = DomainRenamer("slow")(CPU()) 23 | m.submodules.cw = cw 24 | m.submodules.cpu = cpu 25 | m.submodules.memory = memory 26 | 27 | self.cpu = cpu 28 | self.memory = memory 29 | 30 | x1 = Signal(32) 31 | 32 | # Connect memory to CPU 33 | m.d.comb += [ 34 | memory.mem_addr.eq(cpu.mem_addr), 35 | memory.mem_rstrb.eq(cpu.mem_rstrb), 36 | cpu.mem_rdata.eq(memory.mem_rdata) 37 | ] 38 | 39 | # CPU debug output 40 | m.d.comb += [ 41 | x1.eq(cpu.x1), 42 | self.leds.eq(x1[0:5]) 43 | ] 44 | 45 | # Export signals for simulation 46 | def export(signal, name): 47 | if type(signal) is not Signal: 48 | newsig = Signal(signal.shape(), name = name) 49 | m.d.comb += newsig.eq(signal) 50 | else: 51 | newsig = signal 52 | self.ports.append(newsig) 53 | setattr(self, name, newsig) 54 | 55 | if platform is None: 56 | export(ClockSignal("slow"), "slow_clk") 57 | #export(pc, "pc") 58 | #export(instr, "instr") 59 | #export(isALUreg, "isALUreg") 60 | #export(isALUimm, "isALUimm") 61 | #export(isBranch, "isBranch") 62 | #export(isJAL, "isJAL") 63 | #export(isJALR, "isJALR") 64 | #export(isLoad, "isLoad") 65 | #export(isStore, "isStore") 66 | #export(isSystem, "isSystem") 67 | #export(rdId, "rdId") 68 | #export(rs1Id, "rs1Id") 69 | #export(rs2Id, "rs2Id") 70 | #export(Iimm, "Iimm") 71 | #export(Bimm, "Bimm") 72 | #export(Jimm, "Jimm") 73 | #export(funct3, "funct3") 74 | #export(rdId, "rdId") 75 | #export(rs1, "rs1") 76 | #export(rs2, "rs2") 77 | #export(writeBackData, "writeBackData") 78 | #export(writeBackEn, "writeBackEn") 79 | #export(aluOut, "aluOut") 80 | #export((1 << cpu.fsm.state), "state") 81 | 82 | return m 83 | -------------------------------------------------------------------------------- /boards/top.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | import sys 4 | 5 | class Top(Elaboratable): 6 | def __init__(self, leds, uart): 7 | if len(sys.argv) == 1: 8 | print("Usage: {} step_number".format(sys.argv[0])) 9 | exit(1) 10 | step = int(sys.argv[1]) 11 | print("step = {}".format(step)) 12 | self.leds = leds 13 | self.uart = uart 14 | 15 | # TODO: this is messy and should be done with iterating over dirs 16 | if step == 1: 17 | path = "01_blink" 18 | elif step == 2: 19 | path = "02_slower_blinky" 20 | elif step == 3: 21 | path = "03_blink_from_rom" 22 | elif step == 4: 23 | path = "04_instruction_decoder" 24 | elif step == 5: 25 | path = "05_register_bank" 26 | elif step == 6: 27 | path = "06_alu" 28 | elif step == 7: 29 | path = "07_assembler" 30 | elif step == 8: 31 | path = "08_jumps" 32 | elif step == 9: 33 | path = "09_branches" 34 | elif step == 10: 35 | path = "10_lui_auipc" 36 | elif step == 11: 37 | path = "11_modules" 38 | elif step == 12: 39 | path = "12_size_optimisation" 40 | elif step == 13: 41 | path = "13_subroutines" 42 | elif step == 14: 43 | path = "14_subroutines_v2" 44 | elif step == 15: 45 | path = "15_load" 46 | elif step == 16: 47 | path = "16_store" 48 | elif step == 17: 49 | path = "17_memory_map" 50 | elif step == 18: 51 | path = "18_mandelbrot" 52 | else: 53 | print("Invalid step_number {}.".format(step)) 54 | exit(1) 55 | 56 | # add project path to give priority to this project 57 | # and avoid global including "soc" packages 58 | sys.path = [path] + sys.path 59 | from soc import SOC 60 | self.soc = SOC() 61 | 62 | def elaborate(self, platform): 63 | m = Module() 64 | soc = self.soc 65 | leds = self.leds 66 | uart = self.uart 67 | m.submodules.soc = soc 68 | 69 | # We connect the SOC leds signal to the various LEDs on the board. 70 | m.d.comb += [ 71 | leds[0].o.eq(soc.leds[0]), 72 | leds[1].o.eq(soc.leds[1]), 73 | leds[2].o.eq(soc.leds[2]), 74 | leds[3].o.eq(soc.leds[3]), 75 | leds[4].o.eq(soc.leds[4]), 76 | ] 77 | 78 | # The TX port is added only later to the SOC 79 | if hasattr(soc, "tx"): 80 | m.d.comb += [ 81 | uart.tx.eq(soc.tx) 82 | ] 83 | 84 | return m 85 | -------------------------------------------------------------------------------- /16_store/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Memory 6 | from cpu import CPU 7 | 8 | class SOC(Elaboratable): 9 | 10 | def __init__(self): 11 | 12 | self.leds = Signal(5) 13 | 14 | # Signals in this list can easily be plotted as vcd traces 15 | self.ports = [] 16 | 17 | def elaborate(self, platform): 18 | 19 | m = Module() 20 | cw = Clockworks(m) 21 | memory = DomainRenamer("slow")(Memory()) 22 | cpu = DomainRenamer("slow")(CPU()) 23 | m.submodules.cw = cw 24 | m.submodules.cpu = cpu 25 | m.submodules.memory = memory 26 | 27 | self.cpu = cpu 28 | self.memory = memory 29 | 30 | x10 = Signal(32) 31 | 32 | # Connect memory to CPU 33 | m.d.comb += [ 34 | memory.mem_addr.eq(cpu.mem_addr), 35 | memory.mem_rstrb.eq(cpu.mem_rstrb), 36 | memory.mem_wdata.eq(cpu.mem_wdata), 37 | memory.mem_wmask.eq(cpu.mem_wmask), 38 | cpu.mem_rdata.eq(memory.mem_rdata) 39 | ] 40 | 41 | # CPU debug output 42 | m.d.comb += [ 43 | x10.eq(cpu.x10), 44 | self.leds.eq(x10[0:5]) 45 | ] 46 | 47 | # Export signals for simulation 48 | def export(signal, name): 49 | if type(signal) is not Signal: 50 | newsig = Signal(signal.shape(), name = name) 51 | m.d.comb += newsig.eq(signal) 52 | else: 53 | newsig = signal 54 | self.ports.append(newsig) 55 | setattr(self, name, newsig) 56 | 57 | if platform is None: 58 | export(ClockSignal("slow"), "slow_clk") 59 | #export(pc, "pc") 60 | #export(instr, "instr") 61 | #export(isALUreg, "isALUreg") 62 | #export(isALUimm, "isALUimm") 63 | #export(isBranch, "isBranch") 64 | #export(isJAL, "isJAL") 65 | #export(isJALR, "isJALR") 66 | #export(isLoad, "isLoad") 67 | #export(isStore, "isStore") 68 | #export(isSystem, "isSystem") 69 | #export(rdId, "rdId") 70 | #export(rs1Id, "rs1Id") 71 | #export(rs2Id, "rs2Id") 72 | #export(Iimm, "Iimm") 73 | #export(Bimm, "Bimm") 74 | #export(Jimm, "Jimm") 75 | #export(funct3, "funct3") 76 | #export(rdId, "rdId") 77 | #export(rs1, "rs1") 78 | #export(rs2, "rs2") 79 | #export(writeBackData, "writeBackData") 80 | #export(writeBackEn, "writeBackEn") 81 | #export(aluOut, "aluOut") 82 | #export((1 << cpu.fsm.state), "state") 83 | 84 | return m 85 | -------------------------------------------------------------------------------- /17_memory_map/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Mem(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | LI sp, 0x1800 11 | LI gp, 0x400000 12 | 13 | l0: 14 | LI s0, 16 15 | LI a0, 0 16 | 17 | l1: 18 | SW a0, gp, 4 19 | CALL wait 20 | ADDI a0, a0, 1 21 | BNE a0, s0, l1 22 | 23 | LI s0, 26 24 | LI a0, "a" 25 | LI s1, 0 26 | 27 | l2: 28 | CALL putc 29 | ADDI a0, a0, 1 30 | ADDI s1, s1, 1 31 | BNE s1, s0, l2 32 | 33 | LI a0, 13 34 | CALL putc 35 | LI a0, 10 36 | CALL putc 37 | 38 | J l0 39 | 40 | EBREAK 41 | 42 | wait: 43 | LI t0, 1 44 | SLLI t0, t0, 18 45 | 46 | wait_loop: 47 | ADDI t0, t0, -1 48 | BNEZ t0, wait_loop 49 | RET 50 | 51 | putc: 52 | SW a0, gp, 8 53 | LI t0, 0x200 54 | putc_loop: 55 | LW t1, gp, 0x10 56 | AND t1, t1, t0 57 | BNEZ t1, putc_loop 58 | RET 59 | """) 60 | 61 | a.assemble() 62 | self.instructions = a.mem 63 | 64 | # Add 0 memory up to offset 1024 / word 256 65 | while len(self.instructions) < (1024 * 6 / 4): 66 | self.instructions.append(0) 67 | 68 | print("memory = {}".format(self.instructions)) 69 | 70 | # Instruction memory initialised with above instructions 71 | self.mem = Memory(width=32, depth=len(self.instructions), 72 | init=self.instructions, name="mem") 73 | 74 | self.mem_addr = Signal(32) 75 | self.mem_rdata = Signal(32) 76 | self.mem_rstrb = Signal() 77 | self.mem_wdata = Signal(32) 78 | self.mem_wmask = Signal(4) 79 | 80 | def elaborate(self, platform): 81 | m = Module() 82 | 83 | # Using the memory module from amaranth library, 84 | # we can use write_port and read_port to easily instantiate 85 | # platform specific primitives to access memory efficiently. 86 | w_port = m.submodules.w_port = self.mem.write_port( 87 | domain="sync", granularity=8 88 | ) 89 | r_port = m.submodules.r_port = self.mem.read_port( 90 | domain="sync", transparent=False 91 | ) 92 | 93 | word_addr = self.mem_addr[2:32] 94 | 95 | # Hook up read port 96 | m.d.comb += [ 97 | r_port.addr.eq(word_addr), 98 | r_port.en.eq(self.mem_rstrb), 99 | self.mem_rdata.eq(r_port.data) 100 | ] 101 | 102 | # Hook up write port 103 | m.d.comb += [ 104 | w_port.addr.eq(word_addr), 105 | w_port.en.eq(self.mem_wmask), 106 | w_port.data.eq(self.mem_wdata) 107 | ] 108 | 109 | return m 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## learn-fpga (Amaranth HDL version) 2 | 3 | This repository contains code to follow the excellent learn-fpga tutorial by Bruno Levy [from blinker to RISC-V](https://github.com/BrunoLevy/learn-fpga/blob/master/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/README.md) using [Amaranth HDL](https://github.com/amaranth-lang/amaranth) instead of Verilog. 4 | 5 | The tutorial starts from a very simple 'blink' example and will end with a fully functional RISC-V CPU core. 6 | 7 | This code repository is only meant as a supplement to Bruno Levy's main repository. There are very few explanations in this repository that go beyond explaining certain aspects of Amaranth HDL. Please refer to the main tutorial for detailed information of every step of the implementation. 8 | 9 | Using Amaranth HDL the code can certainly be simplified / restructured, but the aim of this project is to keep close to the original. 10 | 11 | Note: The repository is currently undergoing a transition to incorporate language changes from Amaranth HDL 0.5. If you want to use older Amaranth versions, check out the tag 'pre-amaranth-0.5'. 12 | 13 | ### Board support 14 | 15 | Support for the following boards is included: 16 | 17 | * Digilent Arty A7 18 | * Digilent CMOD A7 19 | * Digilent CMOD S7 (untested) 20 | * Sipeed tang nano 9k 21 | 22 | If you don't have a board, you can still run the code in the Amaranth Python simulator. 23 | 24 | 25 | ### Toolchain 26 | 27 | Amaranth HDL supports the following toolchains for the boards: 28 | 29 | * AMD/Xilinx Vivado, proprietary 30 | * F4PGA (former Symbiflow), using FLOSS tools (Yosys, Nextpnr) 31 | * Gowin, proprietary 32 | * Project Apicula (for Gowin FPGAs) 33 | 34 | 35 | ### Running the simulation 36 | 37 | Each directory contains a `bench.py` file. This contains a test bench for the simulator. Run it like so: 38 | 39 | ``` 40 | source env.sh # Add directories to Python library path 41 | cd 01_blink 42 | python bench.py 43 | ``` 44 | 45 | 46 | ### Building a firmware bitfile 47 | 48 | The platform specific code is in the `boards` directory. To build e.g. step 5 for the Arty A7 board: 49 | 50 | ``` 51 | source env.sh # Add directories to Python library path 52 | python boards/digilent_arty_a7.py 5 53 | ``` 54 | 55 | 56 | ### RISC-V assembler 57 | 58 | This repository also contains a (minimal) RISC-V assembler written in Python in the `tools` directory. 59 | 60 | 61 | ### UART connection 62 | 63 | Due to deviation of the internal oscillator frequency from the nominal value it is possible that the UART baudrate is not exactly the same as what is set in the code (by default 1 MBaud). In this case it helps to vary the receiver baudrate by +- 20% and check if reception works. If an oscilloscope is available, you can also measure the clock frequency by patching out the clock signal to one of the pins and measuring the signal period. 64 | 65 | #### Sipeed Tang Nano 9k 66 | 67 | The built-in UART-USB converter does not work very well (at least not on Linux). For this reason, it is better to connect an external UART-USB converter to the Pins 53 (rx) and 54 (tx). When testing the receiver had to be tuned to between 900 kBaud and 960 kBaud. 68 | 69 | ### Licensing 70 | 71 | The files in this repository are licensed under the BSD-3-Clause license. 72 | Exceptions are marked in the respective files. 73 | See the files in the LICENSES directory for details. 74 | 75 | 76 | ### Author 77 | 78 | * *Bastian Löher (bl0x)* 79 | -------------------------------------------------------------------------------- /04_instruction_decoder/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | 6 | class SOC(Elaboratable): 7 | 8 | def __init__(self): 9 | 10 | self.leds = Signal(5) 11 | 12 | # Signals in this list can easily be plotted as vcd traces 13 | self.ports = [] 14 | 15 | def elaborate(self, platform): 16 | 17 | m = Module() 18 | 19 | cw = Clockworks(m, slow=21) 20 | m.submodules.cw = cw 21 | 22 | # Instruction sequence to be executed 23 | sequence = [ 24 | # 24 16 8 0 25 | # .......|.......|.......|.......| 26 | #R rs2 rs1 f3 rd op 27 | #I imm rs1 f3 rd op 28 | #S imm rs2 rs1 f3 imm op 29 | # ......|....|....|..|....|......| 30 | 0b00000000000000000000000000110011, # R add x0, x0, x0 31 | 0b00000000000000000000000010110011, # R add x1, x0, x0 32 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 33 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 34 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 35 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 36 | 0b00000000000000001010000100000011, # I lw x2, 0(x1) 37 | 0b00000000000100010010000000100011, # S sw x2, 0(x1) 38 | 0b00000000000100000000000001110011 # S ebreak 39 | ] 40 | 41 | # Program counter 42 | pc = Signal(32) 43 | 44 | # Current instruction 45 | instr = Signal(32, reset=0b0110011) 46 | 47 | # Instruction memory initialised with above 'sequence' 48 | mem = Array([Signal(32, reset=x) for x in sequence]) 49 | 50 | # Opcode decoder 51 | isALUreg = (instr[0:7] == 0b0110011) 52 | isALUimm = (instr[0:7] == 0b0010011) 53 | isBranch = (instr[0:7] == 0b1100011) 54 | isJALR = (instr[0:7] == 0b1100111) 55 | isJAL = (instr[0:7] == 0b1101111) 56 | isAUIPC = (instr[0:7] == 0b0010111) 57 | isLUI = (instr[0:7] == 0b0110111) 58 | isLoad = (instr[0:7] == 0b0000011) 59 | isStore = (instr[0:7] == 0b0100011) 60 | isSystem = (instr[0:7] == 0b1110011) 61 | 62 | # Immediate format decoder 63 | Uimm = (Cat(Const(0).replicate(12), instr[12:32])) 64 | Iimm = (Cat(instr[20:31], instr[31].replicate(21))) 65 | Simm = (Cat(instr[7:12], instr[25:31], instr[31].replicate(21))), 66 | Bimm = (Cat(0, instr[8:12], instr[25:31], instr[7], instr[31].replicate(20))) 67 | Jimm = (Cat(0, instr[21:31], instr[20], instr[12:20], instr[31].replicate(12))) 68 | 69 | # Register addresses decoder 70 | rs1Id = (instr[15:20]) 71 | rs2Id = (instr[20:25]) 72 | rdId = ( instr[7:12]) 73 | 74 | # Function code decdore 75 | funct3 = (instr[12:15]) 76 | funct7 = (instr[25:32]) 77 | 78 | # Fetch instruction and increase PC 79 | m.d.slow += [ 80 | instr.eq(mem[pc]), 81 | pc.eq(Mux(isSystem, 0, pc + 1)) 82 | ] 83 | 84 | # Assign important signals to LEDS 85 | m.d.comb += self.leds.eq(Mux(isSystem, 31, 86 | Cat(isLoad, isStore, isALUimm, isALUreg, pc[0]))) 87 | 88 | # Export signals for simulation 89 | def export(signal, name): 90 | if type(signal) is not Signal: 91 | newsig = Signal(signal.shape(), name = name) 92 | m.d.comb += newsig.eq(signal) 93 | else: 94 | newsig = signal 95 | self.ports.append(newsig) 96 | setattr(self, name, newsig) 97 | 98 | if platform is None: 99 | export(pc, "pc") 100 | export(instr, "instr") 101 | export(isALUreg, "isALUreg") 102 | export(isALUimm, "isALUimm") 103 | export(isLoad, "isLoad") 104 | export(isStore, "isStore") 105 | export(isSystem, "isSystem") 106 | export(rdId, "rdId") 107 | export(rs1Id, "rs1Id") 108 | export(rs2Id, "rs2Id") 109 | export(Iimm, "Iimm") 110 | export(funct3, "funct3") 111 | 112 | return m 113 | -------------------------------------------------------------------------------- /17_memory_map/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Mem 6 | from cpu import CPU 7 | from uart_tx import UartTx 8 | 9 | class SOC(Elaboratable): 10 | 11 | def __init__(self): 12 | 13 | self.leds = Signal(5) 14 | self.tx = Signal() 15 | 16 | # Signals in this list can easily be plotted as vcd traces 17 | self.ports = [] 18 | 19 | def elaborate(self, platform): 20 | 21 | clk_frequency = int(platform.default_clk_constraint.frequency) 22 | print("clock frequency = {}".format(clk_frequency)) 23 | 24 | m = Module() 25 | cw = Clockworks(m) 26 | memory = DomainRenamer("slow")(Mem()) 27 | cpu = DomainRenamer("slow")(CPU()) 28 | uart_tx = DomainRenamer("slow")( 29 | UartTx(freq_hz=clk_frequency, baud_rate=1000000)) 30 | 31 | m.submodules.cw = cw 32 | m.submodules.cpu = cpu 33 | m.submodules.memory = memory 34 | m.submodules.uart_tx = uart_tx 35 | 36 | self.cpu = cpu 37 | self.memory = memory 38 | 39 | ram_rdata = Signal(32) 40 | mem_wordaddr = Signal(30) 41 | isIO = Signal() 42 | isRAM = Signal() 43 | mem_wstrb = Signal() 44 | io_rdata = Signal(32) 45 | 46 | # Memory map bits 47 | IO_LEDS_bit = 0 48 | IO_UART_DAT_bit = 1 49 | IO_UART_CNTL_bit = 2 50 | 51 | m.d.comb += [ 52 | mem_wordaddr.eq(cpu.mem_addr[2:32]), 53 | isIO.eq(cpu.mem_addr[22]), 54 | isRAM.eq(~isIO), 55 | mem_wstrb.eq(cpu.mem_wmask.any()) 56 | ] 57 | 58 | # Connect memory to CPU 59 | m.d.comb += [ 60 | memory.mem_addr.eq(cpu.mem_addr), 61 | memory.mem_rstrb.eq(isRAM & cpu.mem_rstrb), 62 | memory.mem_wdata.eq(cpu.mem_wdata), 63 | memory.mem_wmask.eq(isRAM.replicate(4) & cpu.mem_wmask), 64 | ram_rdata.eq(memory.mem_rdata), 65 | cpu.mem_rdata.eq(Mux(isRAM, ram_rdata, io_rdata)) 66 | ] 67 | 68 | # LEDs 69 | with m.If(isIO & mem_wstrb & mem_wordaddr[IO_LEDS_bit]): 70 | m.d.sync += self.leds.eq(cpu.mem_wdata) 71 | 72 | # UART 73 | uart_valid = Signal() 74 | uart_ready = Signal() 75 | 76 | m.d.comb += [ 77 | uart_valid.eq(isIO & mem_wstrb & mem_wordaddr[IO_UART_DAT_bit]) 78 | ] 79 | 80 | # Hook up UART 81 | m.d.comb += [ 82 | uart_tx.valid.eq(uart_valid), 83 | uart_tx.data.eq(cpu.mem_wdata[0:8]), 84 | uart_ready.eq(uart_tx.ready), 85 | self.tx.eq(uart_tx.tx) 86 | ] 87 | 88 | # Data from UART 89 | m.d.comb += [ 90 | io_rdata.eq(Mux(mem_wordaddr[IO_UART_CNTL_bit], 91 | Cat(C(0, 9), ~uart_ready, C(0, 22)), C(0, 32))) 92 | ] 93 | 94 | 95 | # Export signals for simulation 96 | def export(signal, name): 97 | if type(signal) is not Signal: 98 | newsig = Signal(signal.shape(), name = name) 99 | m.d.comb += newsig.eq(signal) 100 | else: 101 | newsig = signal 102 | self.ports.append(newsig) 103 | setattr(self, name, newsig) 104 | 105 | if platform is None: 106 | export(ClockSignal("slow"), "slow_clk") 107 | #export(pc, "pc") 108 | #export(instr, "instr") 109 | #export(isALUreg, "isALUreg") 110 | #export(isALUimm, "isALUimm") 111 | #export(isBranch, "isBranch") 112 | #export(isJAL, "isJAL") 113 | #export(isJALR, "isJALR") 114 | #export(isLoad, "isLoad") 115 | #export(isStore, "isStore") 116 | #export(isSystem, "isSystem") 117 | #export(rdId, "rdId") 118 | #export(rs1Id, "rs1Id") 119 | #export(rs2Id, "rs2Id") 120 | #export(Iimm, "Iimm") 121 | #export(Bimm, "Bimm") 122 | #export(Jimm, "Jimm") 123 | #export(funct3, "funct3") 124 | #export(rdId, "rdId") 125 | #export(rs1, "rs1") 126 | #export(rs2, "rs2") 127 | #export(writeBackData, "writeBackData") 128 | #export(writeBackEn, "writeBackEn") 129 | #export(aluOut, "aluOut") 130 | #export((1 << cpu.fsm.state), "state") 131 | 132 | return m 133 | -------------------------------------------------------------------------------- /tests/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Mem 6 | from cpu import CPU 7 | from uart_tx import UartTx 8 | 9 | class SOC(Elaboratable): 10 | 11 | def __init__(self): 12 | 13 | self.leds = Signal(5) 14 | self.tx = Signal() 15 | 16 | # Signals in this list can easily be plotted as vcd traces 17 | self.ports = [] 18 | 19 | def elaborate(self, platform): 20 | 21 | simulation = platform is None 22 | 23 | m = Module() 24 | cw = Clockworks(m) 25 | memory = DomainRenamer("slow")(Mem(simulation=simulation)) 26 | cpu = DomainRenamer("slow")(CPU()) 27 | uart_tx = DomainRenamer("slow")( 28 | UartTx(freq_hz=12*1000000, baud_rate=1000000)) 29 | 30 | m.submodules.cw = cw 31 | m.submodules.cpu = cpu 32 | m.submodules.memory = memory 33 | m.submodules.uart_tx = uart_tx 34 | 35 | self.cpu = cpu 36 | self.memory = memory 37 | 38 | ram_rdata = Signal(32) 39 | mem_wordaddr = Signal(30) 40 | isIO = Signal() 41 | isRAM = Signal() 42 | mem_wstrb = Signal() 43 | io_rdata = Signal(32) 44 | 45 | # Memory map bits 46 | IO_LEDS_bit = 0 47 | IO_UART_DAT_bit = 1 48 | IO_UART_CNTL_bit = 2 49 | 50 | m.d.comb += [ 51 | mem_wordaddr.eq(cpu.mem_addr[2:32]), 52 | isIO.eq(cpu.mem_addr[22]), 53 | isRAM.eq(~isIO), 54 | mem_wstrb.eq(cpu.mem_wmask.any()) 55 | ] 56 | 57 | self.mem_wdata = cpu.mem_wdata 58 | 59 | # Connect memory to CPU 60 | m.d.comb += [ 61 | memory.mem_addr.eq(cpu.mem_addr), 62 | memory.mem_rstrb.eq(isRAM & cpu.mem_rstrb), 63 | memory.mem_wdata.eq(cpu.mem_wdata), 64 | memory.mem_wmask.eq(isRAM.replicate(4) & cpu.mem_wmask), 65 | ram_rdata.eq(memory.mem_rdata), 66 | cpu.mem_rdata.eq(Mux(isRAM, ram_rdata, io_rdata)) 67 | ] 68 | 69 | # LEDs 70 | with m.If(isIO & mem_wstrb & mem_wordaddr[IO_LEDS_bit]): 71 | m.d.sync += self.leds.eq(cpu.mem_wdata) 72 | 73 | # UART 74 | uart_valid = Signal() 75 | self.uart_valid = uart_valid 76 | uart_ready = Signal() 77 | 78 | m.d.comb += [ 79 | uart_valid.eq(isIO & mem_wstrb & mem_wordaddr[IO_UART_DAT_bit]) 80 | ] 81 | 82 | # Hook up UART 83 | m.d.comb += [ 84 | uart_tx.valid.eq(uart_valid), 85 | uart_tx.data.eq(cpu.mem_wdata[0:8]), 86 | uart_ready.eq(uart_tx.ready), 87 | self.tx.eq(uart_tx.tx) 88 | ] 89 | 90 | # Data from UART 91 | m.d.comb += [ 92 | io_rdata.eq(Mux(mem_wordaddr[IO_UART_CNTL_bit], 93 | Cat(C(0, 9), ~uart_ready, C(0, 22)), C(0, 32))) 94 | ] 95 | 96 | 97 | # Export signals for simulation 98 | def export(signal, name): 99 | if type(signal) is not Signal: 100 | newsig = Signal(signal.shape(), name = name) 101 | m.d.comb += newsig.eq(signal) 102 | else: 103 | newsig = signal 104 | self.ports.append(newsig) 105 | setattr(self, name, newsig) 106 | 107 | if platform is None: 108 | export(ClockSignal("slow"), "slow_clk") 109 | #export(pc, "pc") 110 | #export(instr, "instr") 111 | #export(isALUreg, "isALUreg") 112 | #export(isALUimm, "isALUimm") 113 | #export(isBranch, "isBranch") 114 | #export(isJAL, "isJAL") 115 | #export(isJALR, "isJALR") 116 | #export(isLoad, "isLoad") 117 | #export(isStore, "isStore") 118 | #export(isSystem, "isSystem") 119 | #export(rdId, "rdId") 120 | #export(rs1Id, "rs1Id") 121 | #export(rs2Id, "rs2Id") 122 | #export(Iimm, "Iimm") 123 | #export(Bimm, "Bimm") 124 | #export(Jimm, "Jimm") 125 | #export(funct3, "funct3") 126 | #export(rdId, "rdId") 127 | #export(rs1, "rs1") 128 | #export(rs2, "rs2") 129 | #export(writeBackData, "writeBackData") 130 | #export(writeBackEn, "writeBackEn") 131 | #export(aluOut, "aluOut") 132 | #export((1 << cpu.fsm.state), "state") 133 | 134 | return m 135 | -------------------------------------------------------------------------------- /18_mandelbrot/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from memory import Mem 6 | from cpu import CPU 7 | from uart_tx import UartTx 8 | 9 | class SOC(Elaboratable): 10 | 11 | def __init__(self): 12 | 13 | self.leds = Signal(5) 14 | self.tx = Signal() 15 | 16 | # Signals in this list can easily be plotted as vcd traces 17 | self.ports = [] 18 | 19 | def elaborate(self, platform): 20 | 21 | clk_frequency = int(platform.default_clk_constraint.frequency) 22 | print("clock frequency = {}".format(clk_frequency)) 23 | 24 | m = Module() 25 | cw = Clockworks(m) 26 | memory = DomainRenamer("slow")(Mem()) 27 | cpu = DomainRenamer("slow")(CPU()) 28 | uart_tx = DomainRenamer("slow")( 29 | UartTx(freq_hz=clk_frequency, baud_rate=1000000)) 30 | 31 | m.submodules.cw = cw 32 | m.submodules.cpu = cpu 33 | m.submodules.memory = memory 34 | m.submodules.uart_tx = uart_tx 35 | 36 | self.cpu = cpu 37 | self.memory = memory 38 | 39 | ram_rdata = Signal(32) 40 | mem_wordaddr = Signal(30) 41 | isIO = Signal() 42 | isRAM = Signal() 43 | mem_wstrb = Signal() 44 | io_rdata = Signal(32) 45 | 46 | # Memory map bits 47 | IO_LEDS_bit = 0 48 | IO_UART_DAT_bit = 1 49 | IO_UART_CNTL_bit = 2 50 | 51 | m.d.comb += [ 52 | mem_wordaddr.eq(cpu.mem_addr[2:32]), 53 | isIO.eq(cpu.mem_addr[22]), 54 | isRAM.eq(~isIO), 55 | mem_wstrb.eq(cpu.mem_wmask.any()) 56 | ] 57 | 58 | self.mem_wdata = cpu.mem_wdata 59 | 60 | # Connect memory to CPU 61 | m.d.comb += [ 62 | memory.mem_addr.eq(cpu.mem_addr), 63 | memory.mem_rstrb.eq(isRAM & cpu.mem_rstrb), 64 | memory.mem_wdata.eq(cpu.mem_wdata), 65 | memory.mem_wmask.eq(isRAM.replicate(4) & cpu.mem_wmask), 66 | ram_rdata.eq(memory.mem_rdata), 67 | cpu.mem_rdata.eq(Mux(isRAM, ram_rdata, io_rdata)) 68 | ] 69 | 70 | # LEDs 71 | with m.If(isIO & mem_wstrb & mem_wordaddr[IO_LEDS_bit]): 72 | m.d.sync += self.leds.eq(cpu.mem_wdata) 73 | 74 | # UART 75 | uart_valid = Signal() 76 | self.uart_valid = uart_valid 77 | uart_ready = Signal() 78 | 79 | m.d.comb += [ 80 | uart_valid.eq(isIO & mem_wstrb & mem_wordaddr[IO_UART_DAT_bit]) 81 | ] 82 | 83 | # Hook up UART 84 | m.d.comb += [ 85 | uart_tx.valid.eq(uart_valid), 86 | uart_tx.data.eq(cpu.mem_wdata[0:8]), 87 | uart_ready.eq(uart_tx.ready), 88 | self.tx.eq(uart_tx.tx) 89 | ] 90 | 91 | # Data from UART 92 | m.d.comb += [ 93 | io_rdata.eq(Mux(mem_wordaddr[IO_UART_CNTL_bit], 94 | Cat(C(0, 9), ~uart_ready, C(0, 22)), C(0, 32))) 95 | ] 96 | 97 | 98 | # Export signals for simulation 99 | def export(signal, name): 100 | if type(signal) is not Signal: 101 | newsig = Signal(signal.shape(), name = name) 102 | m.d.comb += newsig.eq(signal) 103 | else: 104 | newsig = signal 105 | self.ports.append(newsig) 106 | setattr(self, name, newsig) 107 | 108 | if platform is None: 109 | export(ClockSignal("slow"), "slow_clk") 110 | #export(pc, "pc") 111 | #export(instr, "instr") 112 | #export(isALUreg, "isALUreg") 113 | #export(isALUimm, "isALUimm") 114 | #export(isBranch, "isBranch") 115 | #export(isJAL, "isJAL") 116 | #export(isJALR, "isJALR") 117 | #export(isLoad, "isLoad") 118 | #export(isStore, "isStore") 119 | #export(isSystem, "isSystem") 120 | #export(rdId, "rdId") 121 | #export(rs1Id, "rs1Id") 122 | #export(rs2Id, "rs2Id") 123 | #export(Iimm, "Iimm") 124 | #export(Bimm, "Bimm") 125 | #export(Jimm, "Jimm") 126 | #export(funct3, "funct3") 127 | #export(rdId, "rdId") 128 | #export(rs1, "rs1") 129 | #export(rs2, "rs2") 130 | #export(writeBackData, "writeBackData") 131 | #export(writeBackEn, "writeBackEn") 132 | #export(aluOut, "aluOut") 133 | #export((1 << cpu.fsm.state), "state") 134 | 135 | return m 136 | -------------------------------------------------------------------------------- /05_register_bank/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | 6 | class SOC(Elaboratable): 7 | 8 | def __init__(self): 9 | 10 | self.leds = Signal(5) 11 | 12 | # Signals in this list can easily be plotted as vcd traces 13 | self.ports = [] 14 | 15 | def elaborate(self, platform): 16 | 17 | m = Module() 18 | 19 | cw = Clockworks(m, slow=21, sim_slow=10) 20 | m.submodules.cw = cw 21 | 22 | # Instruction sequence to be executed 23 | sequence = [ 24 | # 24 16 8 0 25 | # .......|.......|.......|.......| 26 | #R rs2 rs1 f3 rd op 27 | #I imm rs1 f3 rd op 28 | #S imm rs2 rs1 f3 imm op 29 | # ......|....|....|..|....|......| 30 | 0b00000000000000000000000000110011, # R add x0, x0, x0 31 | 0b00000000000000000000000010110011, # R add x1, x0, x0 32 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 33 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 34 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 35 | 0b00000000000100001000000010010011, # I addi x1, x1, 1 36 | 0b00000000000000001010000100000011, # I lw x2, 0(x1) 37 | 0b00000000000100010010000000100011, # S sw x2, 0(x1) 38 | 0b00000000000100000000000001110011 # S ebreak 39 | ] 40 | 41 | # Program counter 42 | pc = Signal(32) 43 | 44 | # Current instruction 45 | instr = Signal(32, reset=0b0110011) 46 | 47 | # Instruction memory initialised with above 'sequence' 48 | mem = Array([Signal(32, reset=x) for x in sequence]) 49 | 50 | # Register bank 51 | regs = Array([Signal(32) for x in range(32)]) 52 | rs1 = Signal(32) 53 | rs2 = Signal(32) 54 | 55 | writeBackData = C(0) 56 | writeBackEn = C(0) 57 | 58 | # Opcode decoder 59 | isALUreg = (instr[0:7] == 0b0110011) 60 | isALUimm = (instr[0:7] == 0b0010011) 61 | isBranch = (instr[0:7] == 0b1100011) 62 | isJALR = (instr[0:7] == 0b1100111) 63 | isJAL = (instr[0:7] == 0b1101111) 64 | isAUIPC = (instr[0:7] == 0b0010111) 65 | isLUI = (instr[0:7] == 0b0110111) 66 | isLoad = (instr[0:7] == 0b0000011) 67 | isStore = (instr[0:7] == 0b0100011) 68 | isSystem = (instr[0:7] == 0b1110011) 69 | 70 | # Immediate format decoder 71 | Uimm = (Cat(Const(0).replicate(12), instr[12:32])) 72 | Iimm = (Cat(instr[20:31], instr[31].replicate(21))) 73 | Simm = (Cat(instr[7:12], instr[25:31], instr[31].replicate(21))), 74 | Bimm = (Cat(0, instr[8:12], instr[25:31], instr[7], 75 | instr[31].replicate(20))) 76 | Jimm = (Cat(0, instr[21:31], instr[20], instr[12:20], 77 | instr[31].replicate(12))) 78 | 79 | # Register addresses decoder 80 | rs1Id = (instr[15:20]) 81 | rs2Id = (instr[20:25]) 82 | rdId = ( instr[7:12]) 83 | 84 | # Function code decdore 85 | funct3 = (instr[12:15]) 86 | funct7 = (instr[25:32]) 87 | 88 | # Data write back 89 | with m.If(writeBackEn & (rdId != 0)): 90 | m.d.slow += regs[rdId].eq(writeBackData) 91 | 92 | # Main finite state machine (FSM) 93 | with m.FSM(reset="FETCH_INSTR", domain="slow") as fsm: 94 | with m.State("FETCH_INSTR"): 95 | m.d.slow += instr.eq(mem[pc]) 96 | m.next = "FETCH_REGS" 97 | with m.State("FETCH_REGS"): 98 | m.d.slow += [ 99 | rs1.eq(regs[rs1Id]), 100 | rs2.eq(regs[rs2Id]) 101 | ] 102 | m.next = "EXECUTE" 103 | with m.State("EXECUTE"): 104 | m.d.slow += pc.eq(pc + 1) 105 | m.next = "FETCH_INSTR" 106 | 107 | # Assign important signals to LEDS 108 | # Note: fsm.state is only accessible outside of the FSM context 109 | m.d.comb += self.leds.eq(Mux(isSystem, 31, (1 << fsm.state))) 110 | 111 | # Export signals for simulation 112 | def export(signal, name): 113 | if type(signal) is not Signal: 114 | newsig = Signal(signal.shape(), name = name) 115 | m.d.comb += newsig.eq(signal) 116 | else: 117 | newsig = signal 118 | self.ports.append(newsig) 119 | setattr(self, name, newsig) 120 | 121 | if platform is None: 122 | export(ClockSignal("slow"), "slow_clk") 123 | export(pc, "pc") 124 | export(instr, "instr") 125 | export(isALUreg, "isALUreg") 126 | export(isALUimm, "isALUimm") 127 | export(isLoad, "isLoad") 128 | export(isStore, "isStore") 129 | export(isSystem, "isSystem") 130 | export(rdId, "rdId") 131 | export(rs1Id, "rs1Id") 132 | export(rs2Id, "rs2Id") 133 | export(Iimm, "Iimm") 134 | export(funct3, "funct3") 135 | 136 | return m 137 | -------------------------------------------------------------------------------- /11_modules/cpu.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | class CPU(Elaboratable): 4 | 5 | def __init__(self): 6 | self.mem_addr = Signal(32) 7 | self.mem_rstrb = Signal() 8 | self.mem_rdata = Signal(32) 9 | self.x1 = Signal(32) 10 | self.fsm = None 11 | 12 | def elaborate(self, platform): 13 | m = Module() 14 | 15 | # Program counter 16 | pc = Signal(32) 17 | self.pc = pc 18 | 19 | # Current instruction 20 | instr = Signal(32, reset=0b0110011) 21 | self.instr = instr 22 | 23 | # Register bank 24 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 25 | rs1 = Signal(32) 26 | rs2 = Signal(32) 27 | 28 | # ALU registers 29 | aluOut = Signal(32) 30 | takeBranch = Signal(32) 31 | 32 | # Opcode decoder 33 | isALUreg = (instr[0:7] == 0b0110011) 34 | isALUimm = (instr[0:7] == 0b0010011) 35 | isBranch = (instr[0:7] == 0b1100011) 36 | isJALR = (instr[0:7] == 0b1100111) 37 | isJAL = (instr[0:7] == 0b1101111) 38 | isAUIPC = (instr[0:7] == 0b0010111) 39 | isLUI = (instr[0:7] == 0b0110111) 40 | isLoad = (instr[0:7] == 0b0000011) 41 | isStore = (instr[0:7] == 0b0100011) 42 | isSystem = (instr[0:7] == 0b1110011) 43 | self.isALUreg = isALUreg 44 | self.isALUimm = isALUimm 45 | self.isBranch = isBranch 46 | self.isLoad = isLoad 47 | self.isStore = isStore 48 | self.isSystem = isSystem 49 | 50 | # Immediate format decoder 51 | Uimm = Cat(Const(0).replicate(12), instr[12:32]) 52 | Iimm = Cat(instr[20:31], instr[31].replicate(21)) 53 | Simm = Cat(instr[7:12], instr[25:31], instr[31].replicate(21)) 54 | Bimm = Cat(0, instr[8:12], instr[25:31], instr[7], 55 | instr[31].replicate(20)) 56 | Jimm = Cat(0, instr[21:31], instr[20], instr[12:20], 57 | instr[31].replicate(12)) 58 | self.Iimm = Iimm 59 | 60 | # Register addresses decoder 61 | rs1Id = instr[15:20] 62 | rs2Id = instr[20:25] 63 | rdId = instr[7:12] 64 | 65 | self.rdId = rdId 66 | self.rs1Id = rs1Id 67 | self.rs2Id = rs2Id 68 | 69 | # Function code decdore 70 | funct3 = instr[12:15] 71 | funct7 = instr[25:32] 72 | self.funct3 = funct3 73 | 74 | # ALU 75 | aluIn1 = rs1 76 | aluIn2 = Mux(isALUreg, rs2, Iimm) 77 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 78 | 79 | # Wire memory address to pc 80 | m.d.comb += self.mem_addr.eq(pc) 81 | 82 | with m.Switch(funct3) as alu: 83 | with m.Case(0b000): 84 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 85 | (aluIn1 - aluIn2), (aluIn1 + aluIn2))) 86 | with m.Case(0b001): 87 | m.d.comb += aluOut.eq(aluIn1 << shamt) 88 | with m.Case(0b010): 89 | m.d.comb += aluOut.eq(aluIn1.as_signed() < aluIn2.as_signed()) 90 | with m.Case(0b011): 91 | m.d.comb += aluOut.eq(aluIn1 < aluIn2) 92 | with m.Case(0b100): 93 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 94 | with m.Case(0b101): 95 | m.d.comb += aluOut.eq(Mux( 96 | funct7[5], 97 | (aluIn1.as_signed() >> shamt), # arithmetic right shift 98 | (aluIn1.as_unsigned() >> shamt))) # logical right shift 99 | with m.Case(0b110): 100 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 101 | with m.Case(0b111): 102 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 103 | 104 | with m.Switch(funct3) as alu_branch: 105 | with m.Case(0b000): 106 | m.d.comb += takeBranch.eq(rs1 == rs2) 107 | with m.Case(0b001): 108 | m.d.comb += takeBranch.eq(rs1 != rs2) 109 | with m.Case(0b100): 110 | m.d.comb += takeBranch.eq(rs1.as_signed() < rs2.as_signed()) 111 | with m.Case(0b101): 112 | m.d.comb += takeBranch.eq(rs1.as_signed() >= rs2.as_signed()) 113 | with m.Case(0b110): 114 | m.d.comb += takeBranch.eq(rs1 < rs2) 115 | with m.Case(0b111): 116 | m.d.comb += takeBranch.eq(rs1 >= rs2) 117 | with m.Case("---"): 118 | m.d.comb += takeBranch.eq(0) 119 | 120 | # Next program counter is either next intstruction or depends on 121 | # jump target 122 | nextPc = Mux((isBranch & takeBranch), pc + Bimm, 123 | Mux(isJAL, pc + Jimm, 124 | Mux(isJALR, rs1 + Iimm, 125 | pc + 4))) 126 | 127 | # Main state machine 128 | with m.FSM(reset="FETCH_INSTR") as fsm: 129 | self.fsm = fsm 130 | m.d.comb += self.mem_rstrb.eq(fsm.ongoing("FETCH_INSTR")) 131 | with m.State("FETCH_INSTR"): 132 | m.next = "WAIT_INSTR" 133 | with m.State("WAIT_INSTR"): 134 | m.d.sync += instr.eq(self.mem_rdata) 135 | m.next = ("FETCH_REGS") 136 | with m.State("FETCH_REGS"): 137 | m.d.sync += [ 138 | rs1.eq(regs[rs1Id]), 139 | rs2.eq(regs[rs2Id]) 140 | ] 141 | m.next = "EXECUTE" 142 | with m.State("EXECUTE"): 143 | m.d.sync += pc.eq(nextPc) 144 | m.next = "FETCH_INSTR" 145 | 146 | # Register write back 147 | writeBackData = Mux((isJAL | isJALR), (pc + 4), 148 | Mux(isLUI, Uimm, 149 | Mux(isAUIPC, (pc + Uimm), aluOut))) 150 | writeBackEn = fsm.ongoing("EXECUTE") & ( 151 | isALUreg | 152 | isALUimm | 153 | isLUI | 154 | isAUIPC | 155 | isJAL | 156 | isJALR) 157 | 158 | self.writeBackData = writeBackData 159 | 160 | with m.If(writeBackEn & (rdId != 0)): 161 | m.d.sync += regs[rdId].eq(writeBackData) 162 | # Also assign to debug output to see what is happening 163 | m.d.sync += self.x1.eq(writeBackData) 164 | 165 | return m 166 | -------------------------------------------------------------------------------- /18_mandelbrot/memory.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | from riscv_assembler import RiscvAssembler 3 | 4 | class Mem(Elaboratable): 5 | 6 | def __init__(self): 7 | a = RiscvAssembler() 8 | 9 | a.read("""begin: 10 | 11 | mandel_shift equ 10 12 | mandel_shift_m1 equ 9 13 | mandel_mul equ 1024 ; (1 << mandel_shift) 14 | xmin equ -2048 ; (-2 * mandel_mul) 15 | xmax equ 2028 ; ( 2 * mandel_mul) 16 | ymin equ -2048 ; (-2 * mandel_mul) 17 | ymax equ 2028 ; ( 2 * mandel_mul) 18 | dx equ 51 ; (xmax - xmin) / 80 19 | dy equ 51 ; (ymax - ymin) / 80 20 | norm_max equ 4096 ; (4 << mandel_shift) 21 | io_leds equ 4 ; (2 + 2) 22 | slow_bit equ 18 ; wait (1 << 18) clocks 23 | ; slow_bit equ 1 ; wait (1 << 18) clocks 24 | 25 | LI sp, 0x1800 ; end of RAM, 6 kB 26 | LI gp, 0x400000 ; IO page 27 | 28 | J mandelstart 29 | 30 | colormap: ; colormap data 31 | DATAB " ", ".", ",", ":" 32 | DATAB ";", "o", "x", "%" 33 | DATAB "#", "@", 0, 0 34 | 35 | mandelstart: ; main entry point 36 | 37 | LI s0, 5 ; blink 5 times 38 | blink: 39 | LI a0, 5 40 | SW a0, gp, io_leds 41 | CALL wait 42 | LI a0, 10 43 | SW a0, gp, io_leds 44 | CALL wait 45 | ADDI s0, s0, -1 46 | BNEZ s0, blink 47 | LI a0, 0 48 | SW a0, gp, io_leds 49 | 50 | LI s1, 0 51 | LI s3, xmin 52 | LI s11, 80 53 | 54 | loop_y: 55 | LI s0, 0 56 | LI s2, ymin ; (bug in LI?) 57 | 58 | loop_x: 59 | MV s4, s2 ; z <- c 60 | MV s5, s3 61 | 62 | LI s10, 9 ; iter <- 9 63 | 64 | 65 | loop_z: 66 | MV a0, s4 67 | MV a1, s4 68 | CALL mulsi3 69 | SRLI s6, a0, mandel_shift ; s6=Zrr <- (Zr*Zr) >> mandel_shift 70 | MV a0, s4 71 | MV a1, s5 72 | CALL mulsi3 73 | SRAI s7, a0, mandel_shift_m1 ; s7=Zri <- (Zr*Zi) >> (mandelshift-1) 74 | MV a0, s5 75 | MV a1, s5 76 | CALL mulsi3 77 | SRLI s8, a0, mandel_shift ; s8=Zii <- (Zi*Zi) >> mandelshift 78 | SUB s4, s6, s8 ; s4=Zr <- Zrr - Zii + Cr 79 | ADD s4, s4, s2 80 | ADD s5, s7, s3 ; s5=Zi <- 2*Zri + cr 81 | 82 | ADD s6, s6, s8 ; Exit, if norm > norm_max 83 | LI s7, norm_max 84 | BGT s6, s7, exit_z 85 | 86 | ADDI s10, s10, -1 ; iter-- 87 | BNEZ s10, loop_z 88 | 89 | 90 | exit_z: 91 | LI a0, colormap ; choose color 92 | ADD a0, a0, s10 93 | LBU a0, a0, 0 94 | CALL putc 95 | 96 | ADDI s0, s0, 1 ; Increase x-counter 97 | ADDI s2, s2, dx 98 | BNE s0, s11, loop_x 99 | 100 | LI a0, 13 ; Send line ending 101 | CALL putc 102 | LI a0, 10 103 | CALL putc 104 | 105 | ADDI s1, s1, 1 ; Increase y-counter 106 | ADDI s3, s3, dy 107 | BNE s1, s11, loop_y 108 | 109 | J mandelstart 110 | 111 | EBREAK 112 | 113 | wait: 114 | LI t0, 1 115 | SLLI t0, t0, slow_bit 116 | wait_loop: 117 | ADDI t0, t0, -1 118 | BNEZ t0, wait_loop 119 | RET 120 | 121 | mulsi3: ; integer multiplication 122 | MV a2, a0 123 | LI a0, 0 124 | mulsi3_l0: 125 | ANDI a3, a1, 1 126 | BEQZ a3, mulsi3_l1 127 | ADD a0, a0, a2 128 | mulsi3_l1: 129 | SRLI a1, a1, 1 130 | SLLI a2, a2, 1 131 | BNEZ a1, mulsi3_l0 132 | RET 133 | 134 | putc: ; Send one character 135 | SW a0, gp, 8 ; (1 << IO_UART_DAT_bit + 2) 136 | LI t0, 0x200 ; Test bit 9 (status bit) below 137 | putc_loop: 138 | LW t1, gp, 0x10 ; (1 << IO_UART_CNTL_bit + 2) 139 | AND t1, t1, t0 ; Test 140 | BNEZ t1, putc_loop 141 | RET 142 | 143 | """) 144 | 145 | a.assemble() 146 | self.instructions = a.mem 147 | 148 | # Add 0 memory up to offset 1024 / word 256 149 | while len(self.instructions) < (1024 * 6 / 4): 150 | self.instructions.append(0) 151 | 152 | print("memory = {}".format(self.instructions)) 153 | 154 | # Instruction memory initialised with above instructions 155 | self.mem = Memory(width=32, depth=len(self.instructions), 156 | init=self.instructions, name="mem") 157 | 158 | self.mem_addr = Signal(32) 159 | self.mem_rdata = Signal(32) 160 | self.mem_rstrb = Signal() 161 | self.mem_wdata = Signal(32) 162 | self.mem_wmask = Signal(4) 163 | 164 | def elaborate(self, platform): 165 | m = Module() 166 | 167 | # Using the memory module from amaranth library, 168 | # we can use write_port and read_port to easily instantiate 169 | # platform specific primitives to access memory efficiently. 170 | w_port = m.submodules.w_port = self.mem.write_port( 171 | domain="sync", granularity=8 172 | ) 173 | r_port = m.submodules.r_port = self.mem.read_port( 174 | domain="sync", transparent=False 175 | ) 176 | 177 | word_addr = self.mem_addr[2:32] 178 | 179 | # Hook up read port 180 | m.d.comb += [ 181 | r_port.addr.eq(word_addr), 182 | r_port.en.eq(self.mem_rstrb), 183 | self.mem_rdata.eq(r_port.data) 184 | ] 185 | 186 | # Hook up write port 187 | m.d.comb += [ 188 | w_port.addr.eq(word_addr), 189 | w_port.en.eq(self.mem_wmask), 190 | w_port.data.eq(self.mem_wdata) 191 | ] 192 | 193 | return m 194 | -------------------------------------------------------------------------------- /07_assembler/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from riscv_assembler import RiscvAssembler 6 | 7 | class SOC(Elaboratable): 8 | 9 | def __init__(self): 10 | 11 | self.leds = Signal(5) 12 | 13 | # Signals in this list can easily be plotted as vcd traces 14 | self.ports = [] 15 | 16 | a = RiscvAssembler() 17 | a.read("""begin: 18 | ADD x0, x0, x0 19 | ADD x1, x0, x0 20 | ADDI x1, x1, 1 21 | ADDI x1, x1, 1 22 | ADDI x1, x1, 1 23 | ADDI x1, x1, 1 24 | ADD x2, x1, x0 25 | ADD x3, x1, x2 26 | SRLI x3, x3, 3 27 | SLLI x3, x3, 31 28 | SRAI x3, x3, 5 29 | SRLI x1, x3, 26 30 | EBREAK 31 | """) 32 | 33 | a.assemble() 34 | self.sequence = a.mem 35 | print("memory = {}".format(self.sequence)) 36 | 37 | def elaborate(self, platform): 38 | 39 | m = Module() 40 | 41 | cw = Clockworks(m, slow=21, sim_slow=10) 42 | m.submodules.cw = cw 43 | 44 | # Program counter 45 | pc = Signal(32) 46 | 47 | # Current instruction 48 | instr = Signal(32, reset=0b0110011) 49 | 50 | # Instruction memory initialised with above 'sequence' 51 | mem = Array([Signal(32, reset=x, name="mem") for x in self.sequence]) 52 | 53 | # Register bank 54 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 55 | rs1 = Signal(32) 56 | rs2 = Signal(32) 57 | 58 | # ALU registers 59 | aluOut = Signal(32) 60 | 61 | # Opcode decoder 62 | isALUreg = (instr[0:7] == 0b0110011) 63 | isALUimm = (instr[0:7] == 0b0010011) 64 | isBranch = (instr[0:7] == 0b1100011) 65 | isJALR = (instr[0:7] == 0b1100111) 66 | isJAL = (instr[0:7] == 0b1101111) 67 | isAUIPC = (instr[0:7] == 0b0010111) 68 | isLUI = (instr[0:7] == 0b0110111) 69 | isLoad = (instr[0:7] == 0b0000011) 70 | isStore = (instr[0:7] == 0b0100011) 71 | isSystem = (instr[0:7] == 0b1110011) 72 | 73 | # Immediate format decoder 74 | Uimm = (Cat(Const(0).replicate(12), instr[12:32])) 75 | Iimm = (Cat(instr[20:31], instr[31].replicate(21))) 76 | Simm = (Cat(instr[7:12], instr[25:31], instr[31].replicate(21))), 77 | Bimm = (Cat(0, instr[8:12], instr[25:31], instr[7], instr[31].replicate(20))) 78 | Jimm = (Cat(0, instr[21:31], instr[20], instr[12:20], instr[31].replicate(12))) 79 | 80 | # Register addresses decoder 81 | rs1Id = (instr[15:20]) 82 | rs2Id = (instr[20:25]) 83 | rdId = (instr[7:12]) 84 | 85 | # Function code decdore 86 | funct3 = (instr[12:15]) 87 | funct7 = (instr[25:32]) 88 | 89 | # ALU 90 | aluIn1 = rs1 91 | aluIn2 = Mux(isALUreg, rs2, Iimm) 92 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 93 | 94 | with m.Switch(funct3) as alu: 95 | with m.Case(0b000): 96 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 97 | (aluIn1 - aluIn2), (aluIn1 + aluIn2))) 98 | with m.Case(0b001): 99 | m.d.comb += aluOut.eq(aluIn1 << shamt) 100 | with m.Case(0b010): 101 | m.d.comb += aluOut.eq(aluIn1.as_signed() < aluIn2.as_signed()) 102 | with m.Case(0b011): 103 | m.d.comb += aluOut.eq(aluIn1 < aluIn2) 104 | with m.Case(0b100): 105 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 106 | with m.Case(0b101): 107 | m.d.comb += aluOut.eq(Mux( 108 | funct7[5], 109 | (aluIn1.as_signed() >> shamt), # arithmetic right shift 110 | (aluIn1.as_unsigned() >> shamt))) # logical right shift 111 | with m.Case(0b110): 112 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 113 | with m.Case(0b111): 114 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 115 | 116 | # Main state machine 117 | with m.FSM(reset="FETCH_INSTR", domain="slow") as fsm: 118 | with m.State("FETCH_INSTR"): 119 | m.d.slow += instr.eq(mem[pc[2:32]]) 120 | m.next = "FETCH_REGS" 121 | with m.State("FETCH_REGS"): 122 | m.d.slow += [ 123 | rs1.eq(regs[rs1Id]), 124 | rs2.eq(regs[rs2Id]) 125 | ] 126 | m.next = "EXECUTE" 127 | with m.State("EXECUTE"): 128 | m.d.slow += pc.eq(pc + 4) 129 | m.next = "FETCH_INSTR" 130 | 131 | # Assign important signals to LEDS 132 | # Note: fsm.state is only accessible outside of the FSM context 133 | m.d.comb += self.leds.eq(Mux(isSystem, 31, (1 << fsm.state))) 134 | 135 | # Register write back 136 | writeBackData = aluOut 137 | writeBackEn = fsm.ongoing("EXECUTE") & (isALUreg | isALUimm) 138 | 139 | with m.If(writeBackEn & (rdId != 0)): 140 | m.d.slow += regs[rdId].eq(writeBackData) 141 | 142 | # Export signals for simulation 143 | def export(signal, name): 144 | if type(signal) is not Signal: 145 | newsig = Signal(signal.shape(), name = name) 146 | m.d.comb += newsig.eq(signal) 147 | else: 148 | newsig = signal 149 | self.ports.append(newsig) 150 | setattr(self, name, newsig) 151 | 152 | if platform is None: 153 | export(ClockSignal("slow"), "slow_clk") 154 | export(pc, "pc") 155 | export(instr, "instr") 156 | export(isALUreg, "isALUreg") 157 | export(isALUimm, "isALUimm") 158 | export(isLoad, "isLoad") 159 | export(isStore, "isStore") 160 | export(isSystem, "isSystem") 161 | export(rdId, "rdId") 162 | export(rs1Id, "rs1Id") 163 | export(rs2Id, "rs2Id") 164 | export(Iimm, "Iimm") 165 | export(funct3, "funct3") 166 | export(rdId, "rdId") 167 | export(rs1, "rs1") 168 | export(rs2, "rs2") 169 | export(writeBackData, "writeBackData") 170 | export(writeBackEn, "writeBackEn") 171 | export(aluOut, "aluOut") 172 | export((1 << fsm.state), "state") 173 | 174 | return m 175 | -------------------------------------------------------------------------------- /08_jumps/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from riscv_assembler import RiscvAssembler 6 | 7 | class SOC(Elaboratable): 8 | 9 | def __init__(self): 10 | 11 | self.leds = Signal(5) 12 | 13 | # Signals in this list can easily be plotted as vcd traces 14 | self.ports = [] 15 | 16 | a = RiscvAssembler() 17 | a.read("""begin: 18 | ADD x1, x0, x0 19 | l0: 20 | ADDI x1, x1, 1 21 | JAL x0, l0 22 | EBREAK 23 | """) 24 | 25 | a.assemble() 26 | self.sequence = a.mem 27 | print("memory = {}".format(self.sequence)) 28 | 29 | def elaborate(self, platform): 30 | 31 | m = Module() 32 | 33 | cw = Clockworks(m, slow=21, sim_slow=10) 34 | m.submodules.cw = cw 35 | 36 | # Program counter 37 | pc = Signal(32) 38 | 39 | # Current instruction 40 | instr = Signal(32, reset=0b0110011) 41 | 42 | # Instruction memory initialised with above 'sequence' 43 | mem = Array([Signal(32, reset=x, name="mem") for x in self.sequence]) 44 | 45 | # Register bank 46 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 47 | rs1 = Signal(32) 48 | rs2 = Signal(32) 49 | 50 | # ALU registers 51 | aluOut = Signal(32) 52 | 53 | # Opcode decoder 54 | isALUreg = (instr[0:7] == 0b0110011) 55 | isALUimm = (instr[0:7] == 0b0010011) 56 | isBranch = (instr[0:7] == 0b1100011) 57 | isJALR = (instr[0:7] == 0b1100111) 58 | isJAL = (instr[0:7] == 0b1101111) 59 | isAUIPC = (instr[0:7] == 0b0010111) 60 | isLUI = (instr[0:7] == 0b0110111) 61 | isLoad = (instr[0:7] == 0b0000011) 62 | isStore = (instr[0:7] == 0b0100011) 63 | isSystem = (instr[0:7] == 0b1110011) 64 | 65 | # Immediate format decoder 66 | Uimm = (Cat(Const(0).replicate(12), instr[12:32])) 67 | Iimm = (Cat(instr[20:31], instr[31].replicate(21))) 68 | Simm = (Cat(instr[7:12], instr[25:31], instr[31].replicate(21))), 69 | Bimm = (Cat(0, instr[8:12], instr[25:31], instr[7], instr[31].replicate(20))) 70 | Jimm = (Cat(0, instr[21:31], instr[20], instr[12:20], instr[31].replicate(12))) 71 | 72 | # Register addresses decoder 73 | rs1Id = (instr[15:20]) 74 | rs2Id = (instr[20:25]) 75 | rdId = ( instr[7:12]) 76 | 77 | # Function code decdore 78 | funct3 = (instr[12:15]) 79 | funct7 = (instr[25:32]) 80 | 81 | # ALU 82 | aluIn1 = rs1 83 | aluIn2 = Mux(isALUreg, rs2, Iimm) 84 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 85 | 86 | with m.Switch(funct3) as alu: 87 | with m.Case(0b000): 88 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 89 | (aluIn1 - aluIn2), (aluIn1 + aluIn2))) 90 | with m.Case(0b001): 91 | m.d.comb += aluOut.eq(aluIn1 << shamt) 92 | with m.Case(0b010): 93 | m.d.comb += aluOut.eq(aluIn1.as_signed() < aluIn2.as_signed()) 94 | with m.Case(0b011): 95 | m.d.comb += aluOut.eq(aluIn1 < aluIn2) 96 | with m.Case(0b100): 97 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 98 | with m.Case(0b101): 99 | m.d.comb += aluOut.eq(Mux( 100 | funct7[5], 101 | (aluIn1.as_signed() >> shamt), # arithmetic right shift 102 | (aluIn1.as_unsigned() >> shamt))) # logical right shift 103 | with m.Case(0b110): 104 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 105 | with m.Case(0b111): 106 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 107 | 108 | # Next program counter is either next intstruction or depends on 109 | # jump target 110 | nextPc = Mux(isJAL, pc + Jimm, Mux(isJALR, rs1 + Iimm, pc + 4)) 111 | 112 | # Main state machine 113 | with m.FSM(reset="FETCH_INSTR", domain="slow") as fsm: 114 | with m.State("FETCH_INSTR"): 115 | m.d.slow += instr.eq(mem[pc[2:32]]) 116 | m.next = "FETCH_REGS" 117 | with m.State("FETCH_REGS"): 118 | m.d.slow += [ 119 | rs1.eq(regs[rs1Id]), 120 | rs2.eq(regs[rs2Id]) 121 | ] 122 | m.next = "EXECUTE" 123 | with m.State("EXECUTE"): 124 | m.d.slow += pc.eq(nextPc) 125 | m.next = "FETCH_INSTR" 126 | 127 | # Register write back 128 | writeBackData = Mux((isJAL | isJALR), (pc + 4), aluOut) 129 | writeBackEn = fsm.ongoing("EXECUTE") & ( 130 | isALUreg | 131 | isALUimm | 132 | isJAL | 133 | isJALR) 134 | 135 | with m.If(writeBackEn & (rdId != 0)): 136 | m.d.slow += regs[rdId].eq(writeBackData) 137 | # Assign writeBackData to leds to see what is happening 138 | m.d.slow += self.leds.eq(writeBackData) 139 | 140 | # Export signals for simulation 141 | def export(signal, name): 142 | if type(signal) is not Signal: 143 | newsig = Signal(signal.shape(), name = name) 144 | m.d.comb += newsig.eq(signal) 145 | else: 146 | newsig = signal 147 | self.ports.append(newsig) 148 | setattr(self, name, newsig) 149 | 150 | if platform is None: 151 | export(ClockSignal("slow"), "slow_clk") 152 | export(pc, "pc") 153 | export(instr, "instr") 154 | export(isALUreg, "isALUreg") 155 | export(isALUimm, "isALUimm") 156 | export(isJAL, "isJAL") 157 | export(isJALR, "isJALR") 158 | export(isLoad, "isLoad") 159 | export(isStore, "isStore") 160 | export(isSystem, "isSystem") 161 | export(rdId, "rdId") 162 | export(rs1Id, "rs1Id") 163 | export(rs2Id, "rs2Id") 164 | export(Iimm, "Iimm") 165 | export(funct3, "funct3") 166 | export(rdId, "rdId") 167 | export(rs1, "rs1") 168 | export(rs2, "rs2") 169 | export(writeBackData, "writeBackData") 170 | export(writeBackEn, "writeBackEn") 171 | export(aluOut, "aluOut") 172 | export((1 << fsm.state), "state") 173 | 174 | return m 175 | -------------------------------------------------------------------------------- /12_size_optimisation/cpu.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | class CPU(Elaboratable): 4 | 5 | def __init__(self): 6 | self.mem_addr = Signal(32) 7 | self.mem_rstrb = Signal() 8 | self.mem_rdata = Signal(32) 9 | self.x1 = Signal(32) 10 | self.fsm = None 11 | 12 | def elaborate(self, platform): 13 | m = Module() 14 | 15 | # Program counter 16 | pc = Signal(32) 17 | self.pc = pc 18 | 19 | # Current instruction 20 | instr = Signal(32, reset=0b0110011) 21 | self.instr = instr 22 | 23 | # Register bank 24 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 25 | rs1 = Signal(32) 26 | rs2 = Signal(32) 27 | 28 | # ALU registers 29 | aluOut = Signal(32) 30 | takeBranch = Signal(32) 31 | 32 | # Opcode decoder 33 | isALUreg = (instr[0:7] == 0b0110011) 34 | isALUimm = (instr[0:7] == 0b0010011) 35 | isBranch = (instr[0:7] == 0b1100011) 36 | isJALR = (instr[0:7] == 0b1100111) 37 | isJAL = (instr[0:7] == 0b1101111) 38 | isAUIPC = (instr[0:7] == 0b0010111) 39 | isLUI = (instr[0:7] == 0b0110111) 40 | isLoad = (instr[0:7] == 0b0000011) 41 | isStore = (instr[0:7] == 0b0100011) 42 | isSystem = (instr[0:7] == 0b1110011) 43 | self.isALUreg = isALUreg 44 | self.isALUimm = isALUimm 45 | self.isBranch = isBranch 46 | self.isLoad = isLoad 47 | self.isStore = isStore 48 | self.isSystem = isSystem 49 | 50 | def Extend(x, n): 51 | return [x for i in range(n + 1)] 52 | 53 | # Immediate format decoder 54 | Uimm = Cat(Const(0, 12), instr[12:32]) 55 | Iimm = Cat(instr[20:31], *Extend(instr[31], 21)) 56 | Simm = Cat(instr[7:12], instr[25:31], *Extend(instr[31], 21)) 57 | Bimm = Cat(0, instr[8:12], instr[25:31], instr[7], 58 | *Extend(instr[31], 20)) 59 | Jimm = Cat(0, instr[21:31], instr[20], instr[12:20], 60 | *Extend(instr[31], 12)) 61 | self.Iimm = Iimm 62 | 63 | # Register addresses decoder 64 | rs1Id = instr[15:20] 65 | rs2Id = instr[20:25] 66 | rdId = instr[7:12] 67 | 68 | self.rdId = rdId 69 | self.rs1Id = rs1Id 70 | self.rs2Id = rs2Id 71 | 72 | # Function code decdore 73 | funct3 = instr[12:15] 74 | funct7 = instr[25:32] 75 | self.funct3 = funct3 76 | 77 | # ALU 78 | aluIn1 = rs1 79 | aluIn2 = Mux((isALUreg | isBranch), rs2, Iimm) 80 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 81 | 82 | # Wire memory address to pc 83 | m.d.comb += self.mem_addr.eq(pc) 84 | 85 | aluMinus = Cat(~aluIn1, C(0,1)) + Cat(aluIn2, C(0,1)) + 1 86 | aluPlus = aluIn1 + aluIn2 87 | 88 | EQ = aluMinus[0:32] == 0 89 | LTU = aluMinus[32] 90 | LT = Mux((aluIn1[31] ^ aluIn2[31]), aluIn1[31], aluMinus[32]) 91 | 92 | def flip32(x): 93 | a = [x[i] for i in range(0, 32)] 94 | return Cat(*reversed(a)) 95 | 96 | # TODO: check these again! 97 | shifter_in = Mux(funct3 == 0b001, flip32(aluIn1), aluIn1) 98 | shifter = Cat(shifter_in, (instr[30] & aluIn1[31])) >> aluIn2[0:5] 99 | leftshift = flip32(shifter) 100 | 101 | with m.Switch(funct3) as alu: 102 | with m.Case(0b000): 103 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 104 | aluMinus[0:32], aluPlus)) 105 | with m.Case(0b001): 106 | m.d.comb += aluOut.eq(leftshift) 107 | with m.Case(0b010): 108 | m.d.comb += aluOut.eq(LT) 109 | with m.Case(0b011): 110 | m.d.comb += aluOut.eq(LTU) 111 | with m.Case(0b100): 112 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 113 | with m.Case(0b101): 114 | m.d.comb += aluOut.eq(shifter) 115 | with m.Case(0b110): 116 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 117 | with m.Case(0b111): 118 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 119 | 120 | with m.Switch(funct3) as alu_branch: 121 | with m.Case(0b000): 122 | m.d.comb += takeBranch.eq(EQ) 123 | with m.Case(0b001): 124 | m.d.comb += takeBranch.eq(~EQ) 125 | with m.Case(0b100): 126 | m.d.comb += takeBranch.eq(LT) 127 | with m.Case(0b101): 128 | m.d.comb += takeBranch.eq(~LT) 129 | with m.Case(0b110): 130 | m.d.comb += takeBranch.eq(LTU) 131 | with m.Case(0b111): 132 | m.d.comb += takeBranch.eq(~LTU) 133 | with m.Case("---"): 134 | m.d.comb += takeBranch.eq(0) 135 | 136 | # Next program counter is either next intstruction or depends on 137 | # jump target 138 | pcPlusImm = pc + Mux(instr[3], Jimm[0:32], 139 | Mux(instr[4], Uimm[0:32], 140 | Bimm[0:32])) 141 | pcPlus4 = pc + 4 142 | 143 | nextPc = Mux(((isBranch & takeBranch) | isJAL), pcPlusImm, 144 | Mux(isJALR, Cat(C(0, 1), aluPlus[1:32]), 145 | pcPlus4)) 146 | 147 | # Main state machine 148 | with m.FSM(reset="FETCH_INSTR") as fsm: 149 | self.fsm = fsm 150 | m.d.comb += self.mem_rstrb.eq(fsm.ongoing("FETCH_INSTR")) 151 | with m.State("FETCH_INSTR"): 152 | m.next = "WAIT_INSTR" 153 | with m.State("WAIT_INSTR"): 154 | m.d.sync += instr.eq(self.mem_rdata) 155 | m.next = ("FETCH_REGS") 156 | with m.State("FETCH_REGS"): 157 | m.d.sync += [ 158 | rs1.eq(regs[rs1Id]), 159 | rs2.eq(regs[rs2Id]) 160 | ] 161 | m.next = "EXECUTE" 162 | with m.State("EXECUTE"): 163 | m.d.sync += pc.eq(nextPc) 164 | m.next = "FETCH_INSTR" 165 | 166 | # Register write back 167 | writeBackData = Mux((isJAL | isJALR), pcPlus4, 168 | Mux(isLUI, Uimm, 169 | Mux(isAUIPC, pcPlusImm, aluOut))) 170 | 171 | writeBackEn = fsm.ongoing("EXECUTE") & ~isBranch & ~isStore 172 | 173 | self.writeBackData = writeBackData 174 | 175 | with m.If(writeBackEn & (rdId != 0)): 176 | m.d.sync += regs[rdId].eq(writeBackData) 177 | # Also assign to debug output to see what is happening 178 | m.d.sync += self.x1.eq(writeBackData) 179 | 180 | return m 181 | -------------------------------------------------------------------------------- /13_subroutines/cpu.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | class CPU(Elaboratable): 4 | 5 | def __init__(self): 6 | self.mem_addr = Signal(32) 7 | self.mem_rstrb = Signal() 8 | self.mem_rdata = Signal(32) 9 | self.x10 = Signal(32) 10 | self.fsm = None 11 | 12 | def elaborate(self, platform): 13 | m = Module() 14 | 15 | # Program counter 16 | pc = Signal(32) 17 | self.pc = pc 18 | 19 | # Current instruction 20 | instr = Signal(32, reset=0b0110011) 21 | self.instr = instr 22 | 23 | # Register bank 24 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 25 | rs1 = Signal(32) 26 | rs2 = Signal(32) 27 | 28 | # ALU registers 29 | aluOut = Signal(32) 30 | takeBranch = Signal(32) 31 | 32 | # Opcode decoder 33 | isALUreg = (instr[0:7] == 0b0110011) 34 | isALUimm = (instr[0:7] == 0b0010011) 35 | isBranch = (instr[0:7] == 0b1100011) 36 | isJALR = (instr[0:7] == 0b1100111) 37 | isJAL = (instr[0:7] == 0b1101111) 38 | isAUIPC = (instr[0:7] == 0b0010111) 39 | isLUI = (instr[0:7] == 0b0110111) 40 | isLoad = (instr[0:7] == 0b0000011) 41 | isStore = (instr[0:7] == 0b0100011) 42 | isSystem = Signal() 43 | m.d.comb += isSystem.eq((instr[0:7] == 0b1110011)) 44 | self.isALUreg = isALUreg 45 | self.isALUimm = isALUimm 46 | self.isBranch = isBranch 47 | self.isLoad = isLoad 48 | self.isStore = isStore 49 | self.isSystem = isSystem 50 | 51 | def Extend(x, n): 52 | return [x for i in range(n + 1)] 53 | 54 | # Immediate format decoder 55 | Uimm = Cat(Const(0, 12), instr[12:32]) 56 | Iimm = Cat(instr[20:31], *Extend(instr[31], 21)) 57 | Simm = Cat(instr[7:12], instr[25:31], *Extend(instr[31], 21)) 58 | Bimm = Cat(0, instr[8:12], instr[25:31], instr[7], 59 | *Extend(instr[31], 20)) 60 | Jimm = Cat(0, instr[21:31], instr[20], instr[12:20], 61 | *Extend(instr[31], 12)) 62 | self.Iimm = Iimm 63 | 64 | # Register addresses decoder 65 | rs1Id = instr[15:20] 66 | rs2Id = instr[20:25] 67 | rdId = instr[7:12] 68 | 69 | self.rdId = rdId 70 | self.rs1Id = rs1Id 71 | self.rs2Id = rs2Id 72 | 73 | # Function code decdore 74 | funct3 = instr[12:15] 75 | funct7 = instr[25:32] 76 | self.funct3 = funct3 77 | 78 | # ALU 79 | aluIn1 = rs1 80 | aluIn2 = Mux((isALUreg | isBranch), rs2, Iimm) 81 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 82 | 83 | # Wire memory address to pc 84 | m.d.comb += self.mem_addr.eq(pc) 85 | 86 | aluMinus = Cat(~aluIn1, C(0,1)) + Cat(aluIn2, C(0,1)) + 1 87 | aluPlus = aluIn1 + aluIn2 88 | 89 | EQ = aluMinus[0:32] == 0 90 | LTU = aluMinus[32] 91 | LT = Mux((aluIn1[31] ^ aluIn2[31]), aluIn1[31], aluMinus[32]) 92 | 93 | def flip32(x): 94 | a = [x[i] for i in range(0, 32)] 95 | return Cat(*reversed(a)) 96 | 97 | # TODO: check these again! 98 | shifter_in = Mux(funct3 == 0b001, flip32(aluIn1), aluIn1) 99 | shifter = Cat(shifter_in, (instr[30] & aluIn1[31])) >> aluIn2[0:5] 100 | leftshift = flip32(shifter) 101 | 102 | with m.Switch(funct3) as alu: 103 | with m.Case(0b000): 104 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 105 | aluMinus[0:32], aluPlus)) 106 | with m.Case(0b001): 107 | m.d.comb += aluOut.eq(leftshift) 108 | with m.Case(0b010): 109 | m.d.comb += aluOut.eq(LT) 110 | with m.Case(0b011): 111 | m.d.comb += aluOut.eq(LTU) 112 | with m.Case(0b100): 113 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 114 | with m.Case(0b101): 115 | m.d.comb += aluOut.eq(shifter) 116 | with m.Case(0b110): 117 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 118 | with m.Case(0b111): 119 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 120 | 121 | with m.Switch(funct3) as alu_branch: 122 | with m.Case(0b000): 123 | m.d.comb += takeBranch.eq(EQ) 124 | with m.Case(0b001): 125 | m.d.comb += takeBranch.eq(~EQ) 126 | with m.Case(0b100): 127 | m.d.comb += takeBranch.eq(LT) 128 | with m.Case(0b101): 129 | m.d.comb += takeBranch.eq(~LT) 130 | with m.Case(0b110): 131 | m.d.comb += takeBranch.eq(LTU) 132 | with m.Case(0b111): 133 | m.d.comb += takeBranch.eq(~LTU) 134 | with m.Case("---"): 135 | m.d.comb += takeBranch.eq(0) 136 | 137 | # Next program counter is either next intstruction or depends on 138 | # jump target 139 | pcPlusImm = pc + Mux(instr[3], Jimm[0:32], 140 | Mux(instr[4], Uimm[0:32], 141 | Bimm[0:32])) 142 | pcPlus4 = pc + 4 143 | 144 | nextPc = Mux(((isBranch & takeBranch) | isJAL), pcPlusImm, 145 | Mux(isJALR, Cat(C(0, 1), aluPlus[1:32]), 146 | pcPlus4)) 147 | 148 | # Main state machine 149 | with m.FSM(reset="FETCH_INSTR") as fsm: 150 | self.fsm = fsm 151 | m.d.comb += self.mem_rstrb.eq(fsm.ongoing("FETCH_INSTR")) 152 | with m.State("FETCH_INSTR"): 153 | m.next = "WAIT_INSTR" 154 | with m.State("WAIT_INSTR"): 155 | m.d.sync += instr.eq(self.mem_rdata) 156 | m.next = ("FETCH_REGS") 157 | with m.State("FETCH_REGS"): 158 | m.d.sync += [ 159 | rs1.eq(regs[rs1Id]), 160 | rs2.eq(regs[rs2Id]) 161 | ] 162 | m.next = "EXECUTE" 163 | with m.State("EXECUTE"): 164 | with m.If(~isSystem): 165 | m.d.sync += pc.eq(nextPc) 166 | m.next = "FETCH_INSTR" 167 | 168 | # Register write back 169 | writeBackData = Mux((isJAL | isJALR), pcPlus4, 170 | Mux(isLUI, Uimm, 171 | Mux(isAUIPC, pcPlusImm, aluOut))) 172 | 173 | writeBackEn = fsm.ongoing("EXECUTE") & ~isBranch & ~isStore 174 | 175 | self.writeBackData = writeBackData 176 | 177 | with m.If(writeBackEn & (rdId != 0)): 178 | m.d.sync += regs[rdId].eq(writeBackData) 179 | # Also assign to debug output to see what is happening 180 | with m.If(rdId == 10): 181 | m.d.sync += self.x10.eq(writeBackData) 182 | 183 | return m 184 | -------------------------------------------------------------------------------- /09_branches/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from riscv_assembler import RiscvAssembler 6 | 7 | class SOC(Elaboratable): 8 | 9 | def __init__(self): 10 | 11 | self.leds = Signal(5) 12 | 13 | # Signals in this list can easily be plotted as vcd traces 14 | self.ports = [] 15 | 16 | a = RiscvAssembler() 17 | a.read("""begin: 18 | ADD x1, x0, x0 19 | ADDI x2, x0, 32 20 | l0: 21 | ADDI x1, x1, 1 22 | BNE x1, x2, l0 23 | EBREAK 24 | """) 25 | 26 | a.assemble() 27 | self.sequence = a.mem 28 | print("memory = {}".format(self.sequence)) 29 | 30 | def elaborate(self, platform): 31 | 32 | m = Module() 33 | 34 | cw = Clockworks(m, slow=21, sim_slow=10) 35 | m.submodules.cw = cw 36 | 37 | # Program counter 38 | pc = Signal(32) 39 | 40 | # Current instruction 41 | instr = Signal(32, reset=0b0110011) 42 | 43 | # Instruction memory initialised with above 'sequence' 44 | mem = Array([Signal(32, reset=x, name="mem") for x in self.sequence]) 45 | 46 | # Register bank 47 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 48 | rs1 = Signal(32) 49 | rs2 = Signal(32) 50 | 51 | # ALU registers 52 | aluOut = Signal(32) 53 | takeBranch = Signal(32) 54 | 55 | # Opcode decoder 56 | isALUreg = (instr[0:7] == 0b0110011) 57 | isALUimm = (instr[0:7] == 0b0010011) 58 | isBranch = (instr[0:7] == 0b1100011) 59 | isJALR = (instr[0:7] == 0b1100111) 60 | isJAL = (instr[0:7] == 0b1101111) 61 | isAUIPC = (instr[0:7] == 0b0010111) 62 | isLUI = (instr[0:7] == 0b0110111) 63 | isLoad = (instr[0:7] == 0b0000011) 64 | isStore = (instr[0:7] == 0b0100011) 65 | isSystem = (instr[0:7] == 0b1110011) 66 | 67 | # Immediate format decoder 68 | Uimm = (Cat(Const(0).replicate(12), instr[12:32])) 69 | Iimm = (Cat(instr[20:31], instr[31].replicate(21))) 70 | Simm = (Cat(instr[7:12], instr[25:31], instr[31].replicate(21))), 71 | Bimm = (Cat(0, instr[8:12], instr[25:31], instr[7], instr[31].replicate(20))) 72 | Jimm = (Cat(0, instr[21:31], instr[20], instr[12:20], instr[31].replicate(12))) 73 | 74 | # Register addresses decoder 75 | rs1Id = (instr[15:20]) 76 | rs2Id = (instr[20:25]) 77 | rdId = (instr[7:12]) 78 | 79 | # Function code decdore 80 | funct3 = (instr[12:15]) 81 | funct7 = (instr[25:32]) 82 | 83 | # ALU 84 | aluIn1 = rs1 85 | aluIn2 = Mux(isALUreg, rs2, Iimm) 86 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 87 | 88 | with m.Switch(funct3) as alu: 89 | with m.Case(0b000): 90 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 91 | (aluIn1 - aluIn2), (aluIn1 + aluIn2))) 92 | with m.Case(0b001): 93 | m.d.comb += aluOut.eq(aluIn1 << shamt) 94 | with m.Case(0b010): 95 | m.d.comb += aluOut.eq(aluIn1.as_signed() < aluIn2.as_signed()) 96 | with m.Case(0b011): 97 | m.d.comb += aluOut.eq(aluIn1 < aluIn2) 98 | with m.Case(0b100): 99 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 100 | with m.Case(0b101): 101 | m.d.comb += aluOut.eq(Mux( 102 | funct7[5], 103 | (aluIn1.as_signed() >> shamt), # arithmetic right shift 104 | (aluIn1.as_unsigned() >> shamt))) # logical right shift 105 | with m.Case(0b110): 106 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 107 | with m.Case(0b111): 108 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 109 | 110 | with m.Switch(funct3) as alu_branch: 111 | with m.Case(0b000): 112 | m.d.comb += takeBranch.eq(rs1 == rs2) 113 | with m.Case(0b001): 114 | m.d.comb += takeBranch.eq(rs1 != rs2) 115 | with m.Case(0b100): 116 | m.d.comb += takeBranch.eq(rs1.as_signed() < rs2.as_signed()) 117 | with m.Case(0b101): 118 | m.d.comb += takeBranch.eq(rs1.as_signed() >= rs2.as_signed()) 119 | with m.Case(0b110): 120 | m.d.comb += takeBranch.eq(rs1 < rs2) 121 | with m.Case(0b111): 122 | m.d.comb += takeBranch.eq(rs1 >= rs2) 123 | with m.Case("---"): 124 | m.d.comb += takeBranch.eq(0) 125 | 126 | # Next program counter is either next intstruction or depends on 127 | # jump target 128 | nextPc = Mux((isBranch & takeBranch), pc + Bimm, 129 | Mux(isJAL, pc + Jimm, 130 | Mux(isJALR, rs1 + Iimm, 131 | pc + 4))) 132 | 133 | # Main state machine 134 | with m.FSM(reset="FETCH_INSTR", domain="slow") as fsm: 135 | with m.State("FETCH_INSTR"): 136 | m.d.slow += instr.eq(mem[pc[2:32]]) 137 | m.next = "FETCH_REGS" 138 | with m.State("FETCH_REGS"): 139 | m.d.slow += [ 140 | rs1.eq(regs[rs1Id]), 141 | rs2.eq(regs[rs2Id]) 142 | ] 143 | m.next = "EXECUTE" 144 | with m.State("EXECUTE"): 145 | m.d.slow += pc.eq(nextPc) 146 | m.next = "FETCH_INSTR" 147 | 148 | # Register write back 149 | writeBackData = Mux((isJAL | isJALR), (pc + 4), aluOut) 150 | writeBackEn = fsm.ongoing("EXECUTE") & ( 151 | isALUreg | 152 | isALUimm | 153 | isJAL | 154 | isJALR) 155 | 156 | with m.If(writeBackEn & (rdId != 0)): 157 | m.d.slow += regs[rdId].eq(writeBackData) 158 | # Assign writeBackData to leds to see what is happening 159 | m.d.slow += self.leds.eq(writeBackData) 160 | 161 | # Export signals for simulation 162 | def export(signal, name): 163 | if type(signal) is not Signal: 164 | newsig = Signal(signal.shape(), name = name) 165 | m.d.comb += newsig.eq(signal) 166 | else: 167 | newsig = signal 168 | self.ports.append(newsig) 169 | setattr(self, name, newsig) 170 | 171 | if platform is None: 172 | export(ClockSignal("slow"), "slow_clk") 173 | export(pc, "pc") 174 | export(instr, "instr") 175 | export(isALUreg, "isALUreg") 176 | export(isALUimm, "isALUimm") 177 | export(isBranch, "isBranch") 178 | export(isJAL, "isJAL") 179 | export(isJALR, "isJALR") 180 | export(isLoad, "isLoad") 181 | export(isStore, "isStore") 182 | export(isSystem, "isSystem") 183 | export(rs1Id, "rs1Id") 184 | export(rs2Id, "rs2Id") 185 | export(Iimm, "Iimm") 186 | export(Bimm, "Bimm") 187 | export(Jimm, "Jimm") 188 | export(funct3, "funct3") 189 | export(rdId, "rdId") 190 | export(rs1, "rs1") 191 | export(rs2, "rs2") 192 | export(writeBackData, "writeBackData") 193 | export(writeBackEn, "writeBackEn") 194 | export(aluOut, "aluOut") 195 | export((1 << fsm.state), "state") 196 | 197 | return m 198 | -------------------------------------------------------------------------------- /14_subroutines_v2/cpu.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | class CPU(Elaboratable): 4 | 5 | def __init__(self): 6 | self.mem_addr = Signal(32) 7 | self.mem_rstrb = Signal() 8 | self.mem_rdata = Signal(32) 9 | self.x10 = Signal(32) 10 | self.fsm = None 11 | 12 | def elaborate(self, platform): 13 | m = Module() 14 | 15 | # Program counter 16 | pc = Signal(32) 17 | self.pc = pc 18 | 19 | # Current instruction 20 | instr = Signal(32, reset=0b0110011) 21 | self.instr = instr 22 | 23 | # Register bank 24 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 25 | rs1 = Signal(32) 26 | rs2 = Signal(32) 27 | 28 | # ALU registers 29 | aluOut = Signal(32) 30 | takeBranch = Signal(32) 31 | 32 | # Opcode decoder 33 | # It is nice to have these as actual signals for simulation 34 | isALUreg = Signal() 35 | isALUimm = Signal() 36 | isBranch = Signal() 37 | isJALR = Signal() 38 | isJAL = Signal() 39 | isAUIPC = Signal() 40 | isLUI = Signal() 41 | isLoad = Signal() 42 | isStore = Signal() 43 | isSystem = Signal() 44 | m.d.comb += [ 45 | isALUreg.eq(instr[0:7] == 0b0110011), 46 | isALUimm.eq(instr[0:7] == 0b0010011), 47 | isBranch.eq(instr[0:7] == 0b1100011), 48 | isJALR.eq(instr[0:7] == 0b1100111), 49 | isJAL.eq(instr[0:7] == 0b1101111), 50 | isAUIPC.eq(instr[0:7] == 0b0010111), 51 | isLUI.eq(instr[0:7] == 0b0110111), 52 | isLoad.eq(instr[0:7] == 0b0000011), 53 | isStore.eq(instr[0:7] == 0b0100011), 54 | isSystem.eq(instr[0:7] == 0b1110011) 55 | ] 56 | self.isALUreg = isALUreg 57 | self.isALUimm = isALUimm 58 | self.isBranch = isBranch 59 | self.isLoad = isLoad 60 | self.isStore = isStore 61 | self.isSystem = isSystem 62 | 63 | def Extend(x, n): 64 | return [x for i in range(n + 1)] 65 | 66 | # Immediate format decoder 67 | Uimm = Cat(Const(0, 12), instr[12:32]) 68 | Iimm = Cat(instr[20:31], *Extend(instr[31], 21)) 69 | Simm = Cat(instr[7:12], instr[25:31], *Extend(instr[31], 21)) 70 | Bimm = Cat(0, instr[8:12], instr[25:31], instr[7], 71 | *Extend(instr[31], 20)) 72 | Jimm = Cat(0, instr[21:31], instr[20], instr[12:20], 73 | *Extend(instr[31], 12)) 74 | self.Iimm = Iimm 75 | 76 | # Register addresses decoder 77 | rs1Id = instr[15:20] 78 | rs2Id = instr[20:25] 79 | rdId = instr[7:12] 80 | 81 | self.rdId = rdId 82 | self.rs1Id = rs1Id 83 | self.rs2Id = rs2Id 84 | 85 | # Function code decdore 86 | funct3 = instr[12:15] 87 | funct7 = instr[25:32] 88 | self.funct3 = funct3 89 | 90 | # ALU 91 | aluIn1 = Signal.like(rs1) 92 | aluIn2 = Signal.like(rs2) 93 | shamt = Signal(5) 94 | aluMinus = Signal(33) 95 | aluPlus = Signal.like(aluIn1) 96 | 97 | m.d.comb += [ 98 | aluIn1.eq(rs1), 99 | aluIn2.eq(Mux((isALUreg | isBranch), rs2, Iimm)), 100 | shamt.eq(Mux(isALUreg, rs2[0:5], instr[20:25])) 101 | ] 102 | 103 | # Wire memory address to pc 104 | m.d.comb += self.mem_addr.eq(pc) 105 | 106 | m.d.comb += [ 107 | aluMinus.eq(Cat(~aluIn1, C(0,1)) + Cat(aluIn2, C(0,1)) + 1), 108 | aluPlus.eq(aluIn1 + aluIn2) 109 | ] 110 | 111 | EQ = aluMinus[0:32] == 0 112 | LTU = aluMinus[32] 113 | LT = Mux((aluIn1[31] ^ aluIn2[31]), aluIn1[31], aluMinus[32]) 114 | 115 | def flip32(x): 116 | a = [x[i] for i in range(0, 32)] 117 | return Cat(*reversed(a)) 118 | 119 | # TODO: check these again! 120 | shifter_in = Mux(funct3 == 0b001, flip32(aluIn1), aluIn1) 121 | shifter = Cat(shifter_in, (instr[30] & aluIn1[31])) >> aluIn2[0:5] 122 | leftshift = flip32(shifter) 123 | 124 | with m.Switch(funct3) as alu: 125 | with m.Case(0b000): 126 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 127 | aluMinus[0:32], aluPlus)) 128 | with m.Case(0b001): 129 | m.d.comb += aluOut.eq(leftshift) 130 | with m.Case(0b010): 131 | m.d.comb += aluOut.eq(LT) 132 | with m.Case(0b011): 133 | m.d.comb += aluOut.eq(LTU) 134 | with m.Case(0b100): 135 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 136 | with m.Case(0b101): 137 | m.d.comb += aluOut.eq(shifter) 138 | with m.Case(0b110): 139 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 140 | with m.Case(0b111): 141 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 142 | 143 | with m.Switch(funct3) as alu_branch: 144 | with m.Case(0b000): 145 | m.d.comb += takeBranch.eq(EQ) 146 | with m.Case(0b001): 147 | m.d.comb += takeBranch.eq(~EQ) 148 | with m.Case(0b100): 149 | m.d.comb += takeBranch.eq(LT) 150 | with m.Case(0b101): 151 | m.d.comb += takeBranch.eq(~LT) 152 | with m.Case(0b110): 153 | m.d.comb += takeBranch.eq(LTU) 154 | with m.Case(0b111): 155 | m.d.comb += takeBranch.eq(~LTU) 156 | with m.Case("---"): 157 | m.d.comb += takeBranch.eq(0) 158 | 159 | # Next program counter is either next intstruction or depends on 160 | # jump target 161 | pcPlusImm = pc + Mux(instr[3], Jimm[0:32], 162 | Mux(instr[4], Uimm[0:32], 163 | Bimm[0:32])) 164 | pcPlus4 = pc + 4 165 | 166 | nextPc = Mux(((isBranch & takeBranch) | isJAL), pcPlusImm, 167 | Mux(isJALR, Cat(C(0, 1), aluPlus[1:32]), 168 | pcPlus4)) 169 | 170 | # Main state machine 171 | with m.FSM(reset="FETCH_INSTR") as fsm: 172 | self.fsm = fsm 173 | m.d.comb += self.mem_rstrb.eq(fsm.ongoing("FETCH_INSTR")) 174 | with m.State("FETCH_INSTR"): 175 | m.next = "WAIT_INSTR" 176 | with m.State("WAIT_INSTR"): 177 | m.d.sync += instr.eq(self.mem_rdata) 178 | m.next = ("FETCH_REGS") 179 | with m.State("FETCH_REGS"): 180 | m.d.sync += [ 181 | rs1.eq(regs[rs1Id]), 182 | rs2.eq(regs[rs2Id]) 183 | ] 184 | m.next = "EXECUTE" 185 | with m.State("EXECUTE"): 186 | with m.If(~isSystem): 187 | m.d.sync += pc.eq(nextPc) 188 | m.next = "FETCH_INSTR" 189 | 190 | # Register write back 191 | writeBackData = Mux((isJAL | isJALR), pcPlus4, 192 | Mux(isLUI, Uimm, 193 | Mux(isAUIPC, pcPlusImm, aluOut))) 194 | 195 | writeBackEn = fsm.ongoing("EXECUTE") & ~isBranch & ~isStore 196 | 197 | self.writeBackData = writeBackData 198 | 199 | with m.If(writeBackEn & (rdId != 0)): 200 | m.d.sync += regs[rdId].eq(writeBackData) 201 | # Also assign to debug output to see what is happening 202 | with m.If(rdId == 10): 203 | m.d.sync += self.x10.eq(writeBackData) 204 | 205 | return m 206 | -------------------------------------------------------------------------------- /10_lui_auipc/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | from riscv_assembler import RiscvAssembler 6 | 7 | class SOC(Elaboratable): 8 | 9 | def __init__(self): 10 | 11 | self.leds = Signal(5) 12 | 13 | # Signals in this list can easily be plotted as vcd traces 14 | self.ports = [] 15 | 16 | a = RiscvAssembler() 17 | a.read("""begin: 18 | LUI x1, 0b11111111111111111111111111111111 19 | ORI x1, x1, 0b11111111111111111111111111111111 20 | EBREAK 21 | """) 22 | 23 | a.assemble() 24 | self.sequence = a.mem 25 | print("memory = {}".format(self.sequence)) 26 | 27 | def elaborate(self, platform): 28 | 29 | m = Module() 30 | 31 | cw = Clockworks(m, slow=21, sim_slow=10) 32 | m.submodules.cw = cw 33 | 34 | # Program counter 35 | pc = Signal(32) 36 | 37 | # Current instruction 38 | instr = Signal(32, reset=0b0110011) 39 | 40 | # Instruction memory initialised with above 'sequence' 41 | mem = Array([Signal(32, reset=x, name="mem") for x in self.sequence]) 42 | 43 | # Register bank 44 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 45 | rs1 = Signal(32) 46 | rs2 = Signal(32) 47 | 48 | # ALU registers 49 | aluOut = Signal(32) 50 | takeBranch = Signal(32) 51 | 52 | # Opcode decoder 53 | isALUreg = (instr[0:7] == 0b0110011) 54 | isALUimm = (instr[0:7] == 0b0010011) 55 | isBranch = (instr[0:7] == 0b1100011) 56 | isJALR = (instr[0:7] == 0b1100111) 57 | isJAL = (instr[0:7] == 0b1101111) 58 | isAUIPC = (instr[0:7] == 0b0010111) 59 | isLUI = (instr[0:7] == 0b0110111) 60 | isLoad = (instr[0:7] == 0b0000011) 61 | isStore = (instr[0:7] == 0b0100011) 62 | isSystem = (instr[0:7] == 0b1110011) 63 | 64 | # Immediate format decoder 65 | Uimm = (Cat(Const(0, 12), instr[12:32])) 66 | Iimm = (Cat(instr[20:31], instr[31].replicate(21))) 67 | Simm = (Cat(instr[7:12], instr[25:31], instr[31].replicate(21))), 68 | Bimm = (Cat(0, instr[8:12], instr[25:31], instr[7], instr[31].replicate(20))) 69 | Jimm = (Cat(0, instr[21:31], instr[20], instr[12:20], instr[31].replicate(12))) 70 | 71 | # Register addresses decoder 72 | rs1Id = (instr[15:20]) 73 | rs2Id = (instr[20:25]) 74 | rdId = ( instr[7:12]) 75 | 76 | # Function code decdore 77 | funct3 = (instr[12:15]) 78 | funct7 = (instr[25:32]) 79 | 80 | # ALU 81 | aluIn1 = rs1 82 | aluIn2 = Mux(isALUreg, rs2, Iimm) 83 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 84 | 85 | with m.Switch(funct3) as alu: 86 | with m.Case(0b000): 87 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 88 | (aluIn1 - aluIn2), (aluIn1 + aluIn2))) 89 | with m.Case(0b001): 90 | m.d.comb += aluOut.eq(aluIn1 << shamt) 91 | with m.Case(0b010): 92 | m.d.comb += aluOut.eq(aluIn1.as_signed() < aluIn2.as_signed()) 93 | with m.Case(0b011): 94 | m.d.comb += aluOut.eq(aluIn1 < aluIn2) 95 | with m.Case(0b100): 96 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 97 | with m.Case(0b101): 98 | m.d.comb += aluOut.eq(Mux( 99 | funct7[5], 100 | (aluIn1.as_signed() >> shamt), # arithmetic right shift 101 | (aluIn1.as_unsigned() >> shamt))) # logical right shift 102 | with m.Case(0b110): 103 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 104 | with m.Case(0b111): 105 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 106 | 107 | with m.Switch(funct3) as alu_branch: 108 | with m.Case(0b000): 109 | m.d.comb += takeBranch.eq(rs1 == rs2) 110 | with m.Case(0b001): 111 | m.d.comb += takeBranch.eq(rs1 != rs2) 112 | with m.Case(0b100): 113 | m.d.comb += takeBranch.eq(rs1.as_signed() < rs2.as_signed()) 114 | with m.Case(0b101): 115 | m.d.comb += takeBranch.eq(rs1.as_signed() >= rs2.as_signed()) 116 | with m.Case(0b110): 117 | m.d.comb += takeBranch.eq(rs1 < rs2) 118 | with m.Case(0b111): 119 | m.d.comb += takeBranch.eq(rs1 >= rs2) 120 | with m.Case("---"): 121 | m.d.comb += takeBranch.eq(0) 122 | 123 | # Next program counter is either next intstruction or depends on 124 | # jump target 125 | nextPc = Mux((isBranch & takeBranch), pc + Bimm, 126 | Mux(isJAL, pc + Jimm, 127 | Mux(isJALR, rs1 + Iimm, 128 | pc + 4))) 129 | 130 | # Main state machine 131 | with m.FSM(reset="FETCH_INSTR", domain="slow") as fsm: 132 | with m.State("FETCH_INSTR"): 133 | m.d.slow += instr.eq(mem[pc[2:32]]) 134 | m.next = "FETCH_REGS" 135 | with m.State("FETCH_REGS"): 136 | m.d.slow += [ 137 | rs1.eq(regs[rs1Id]), 138 | rs2.eq(regs[rs2Id]) 139 | ] 140 | m.next = "EXECUTE" 141 | with m.State("EXECUTE"): 142 | m.d.slow += pc.eq(nextPc) 143 | m.next = "FETCH_INSTR" 144 | 145 | # Register write back 146 | writeBackData = Mux((isJAL | isJALR), (pc + 4), 147 | Mux(isLUI, Uimm, 148 | Mux(isAUIPC, (pc + Uimm), aluOut))) 149 | writeBackEn = fsm.ongoing("EXECUTE") & ( 150 | isALUreg | 151 | isALUimm | 152 | isLUI | 153 | isAUIPC | 154 | isJAL | 155 | isJALR) 156 | 157 | with m.If(writeBackEn & (rdId != 0)): 158 | m.d.slow += regs[rdId].eq(writeBackData) 159 | # Assign writeBackData to leds to see what is happening 160 | m.d.slow += self.leds.eq(writeBackData) 161 | 162 | # Export signals for simulation 163 | def export(signal, name): 164 | if type(signal) is not Signal: 165 | newsig = Signal(signal.shape(), name = name) 166 | m.d.comb += newsig.eq(signal) 167 | else: 168 | newsig = signal 169 | self.ports.append(newsig) 170 | setattr(self, name, newsig) 171 | 172 | if platform is None: 173 | export(ClockSignal("slow"), "slow_clk") 174 | export(pc, "pc") 175 | export(instr, "instr") 176 | export(isALUreg, "isALUreg") 177 | export(isALUimm, "isALUimm") 178 | export(isBranch, "isBranch") 179 | export(isJAL, "isJAL") 180 | export(isJALR, "isJALR") 181 | export(isLoad, "isLoad") 182 | export(isStore, "isStore") 183 | export(isSystem, "isSystem") 184 | export(rs1Id, "rs1Id") 185 | export(rs2Id, "rs2Id") 186 | export(Iimm, "Iimm") 187 | export(Bimm, "Bimm") 188 | export(Jimm, "Jimm") 189 | export(funct3, "funct3") 190 | export(rdId, "rdId") 191 | export(rs1, "rs1") 192 | export(rs2, "rs2") 193 | export(writeBackData, "writeBackData") 194 | export(writeBackEn, "writeBackEn") 195 | export(aluOut, "aluOut") 196 | export((1 << fsm.state), "state") 197 | 198 | return m 199 | -------------------------------------------------------------------------------- /06_alu/soc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from amaranth import * 3 | 4 | from clockworks import Clockworks 5 | 6 | class SOC(Elaboratable): 7 | 8 | def __init__(self): 9 | 10 | self.leds = Signal(5) 11 | 12 | # Signals in this list can easily be plotted as vcd traces 13 | self.ports = [] 14 | 15 | def elaborate(self, platform): 16 | 17 | m = Module() 18 | 19 | cw = Clockworks(m, slow=21, sim_slow=10) 20 | m.submodules.cw = cw 21 | 22 | # Instruction sequence to be executed 23 | sequence = [ 24 | # 24 16 8 0 25 | # .......|.......|.......|.......| 26 | #R rs2 rs1 f3 rd op 27 | #I imm rs1 f3 rd op 28 | #S imm rs2 rs1 f3 imm op 29 | # ......|....|....|..|....|......| 30 | # add x1, x0, x0 31 | # rs2 rs1 add rd ALUREG 32 | # -> x1 = 0 33 | 0b00000000000000000000000010110011, 34 | # addi x1, x1, 1 35 | # imm rs1 add rd ALUIMM 36 | # -> x1 = 1 37 | 0b00000000000100001000000010010011, 38 | # addi x1, x1, 1 39 | # imm rs1 add rd ALUIMM 40 | # -> x1 = 2 41 | 0b00000000000100001000000010010011, 42 | # addi x1, x1, 1 43 | # imm rs1 add rd ALUIMM 44 | # -> x1 = 3 45 | 0b00000000000100001000000010010011, 46 | # addi x1, x1, 1 47 | # imm rs1 add rd ALUIMM 48 | # -> x1 = 4 49 | 0b00000000000100001000000010010011, 50 | # add x2, x1, x0 51 | # rs2 rs1 add rd ALUREG 52 | # -> x2 = 4 53 | 0b00000000000000001000000100110011, 54 | # add x3, x1, x2 55 | # rs2 rs1 add rd ALUREG 56 | # -> x3 = 8 57 | 0b00000000001000001000000110110011, 58 | # srli x3, x3, 3 59 | # shamt rs1 sr rd ALUIMM 60 | # -> x3 = 1 61 | 0b00000000001100011101000110010011, 62 | # slli x3, x3, 31 63 | # shamt rs1 sl rd ALUIMM 64 | # -> x3 = 0x80000000 65 | 0b00000001111100011001000110010011, 66 | # srai x3, x3, 5 67 | # shamt rs1 sr rd ALUIMM 68 | # -> x3 = 0xfc000000 69 | 0b01000000010100011101000110010011, 70 | # srli x1, x3, 26 71 | # shamt rs1 sr rd ALUIMM 72 | # -> x1 = 0x3f 73 | 0b00000001101000011101000010010011, 74 | 75 | 0b00000000000100000000000001110011 # S ebreak 76 | ] 77 | 78 | # Program counter 79 | pc = Signal(32) 80 | 81 | # Current instruction 82 | instr = Signal(32, reset=0b0110011) 83 | 84 | # Instruction memory initialised with above 'sequence' 85 | mem = Array([Signal(32, reset=x, name="mem") for x in sequence]) 86 | 87 | # Register bank 88 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 89 | rs1 = Signal(32) 90 | rs2 = Signal(32) 91 | 92 | # ALU registers 93 | aluOut = Signal(32) 94 | 95 | # Opcode decoder 96 | isALUreg = (instr[0:7] == 0b0110011) 97 | isALUimm = (instr[0:7] == 0b0010011) 98 | isBranch = (instr[0:7] == 0b1100011) 99 | isJALR = (instr[0:7] == 0b1100111) 100 | isJAL = (instr[0:7] == 0b1101111) 101 | isAUIPC = (instr[0:7] == 0b0010111) 102 | isLUI = (instr[0:7] == 0b0110111) 103 | isLoad = (instr[0:7] == 0b0000011) 104 | isStore = (instr[0:7] == 0b0100011) 105 | isSystem = (instr[0:7] == 0b1110011) 106 | 107 | # Immediate format decoder 108 | Uimm = (Cat(Const(0).replicate(12), instr[12:32])) 109 | Iimm = (Cat(instr[20:31], instr[31].replicate(21))) 110 | Simm = (Cat(instr[7:12], instr[25:31], instr[31].replicate(21))), 111 | Bimm = (Cat(0, instr[8:12], instr[25:31], instr[7], instr[31].replicate(20))) 112 | Jimm = (Cat(0, instr[21:31], instr[20], instr[12:20], instr[31].replicate(12))) 113 | 114 | # Register addresses decoder 115 | rs1Id = (instr[15:20]) 116 | rs2Id = (instr[20:25]) 117 | rdId = ( instr[7:12]) 118 | 119 | # Function code decdore 120 | funct3 = (instr[12:15]) 121 | funct7 = (instr[25:32]) 122 | 123 | # ALU 124 | aluIn1 = rs1 125 | aluIn2 = Mux(isALUreg, rs2, Iimm) 126 | shamt = Mux(isALUreg, rs2[0:5], instr[20:25]) 127 | 128 | with m.Switch(funct3) as alu: 129 | with m.Case(0b000): 130 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 131 | (aluIn1 - aluIn2), (aluIn1 + aluIn2))) 132 | with m.Case(0b001): 133 | m.d.comb += aluOut.eq(aluIn1 << shamt) 134 | with m.Case(0b010): 135 | m.d.comb += aluOut.eq(aluIn1.as_signed() < aluIn2.as_signed()) 136 | with m.Case(0b011): 137 | m.d.comb += aluOut.eq(aluIn1 < aluIn2) 138 | with m.Case(0b100): 139 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 140 | with m.Case(0b101): 141 | m.d.comb += aluOut.eq(Mux( 142 | funct7[5], 143 | (aluIn1.as_signed() >> shamt), # arithmetic right shift 144 | (aluIn1.as_unsigned() >> shamt))) # logical right shift 145 | with m.Case(0b110): 146 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 147 | with m.Case(0b111): 148 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 149 | 150 | # Main state machine 151 | with m.FSM(reset="FETCH_INSTR", domain="slow") as fsm: 152 | with m.State("FETCH_INSTR"): 153 | m.d.slow += instr.eq(mem[pc]) 154 | m.next = "FETCH_REGS" 155 | with m.State("FETCH_REGS"): 156 | m.d.slow += [ 157 | rs1.eq(regs[rs1Id]), 158 | rs2.eq(regs[rs2Id]) 159 | ] 160 | m.next = "EXECUTE" 161 | with m.State("EXECUTE"): 162 | m.d.slow += pc.eq(pc + 1) 163 | m.next = "FETCH_INSTR" 164 | 165 | # Assign important signals to LEDS 166 | # Note: fsm.state is only accessible outside of the FSM context 167 | m.d.comb += self.leds.eq(Mux(isSystem, 31, (1 << fsm.state))) 168 | 169 | # Register write back 170 | writeBackData = aluOut 171 | writeBackEn = fsm.ongoing("EXECUTE") & (isALUreg | isALUimm) 172 | 173 | with m.If(writeBackEn & (rdId != 0)): 174 | m.d.slow += regs[rdId].eq(writeBackData) 175 | 176 | # Export signals for simulation 177 | def export(signal, name): 178 | if type(signal) is not Signal: 179 | newsig = Signal(signal.shape(), name = name) 180 | m.d.comb += newsig.eq(signal) 181 | else: 182 | newsig = signal 183 | self.ports.append(newsig) 184 | setattr(self, name, newsig) 185 | 186 | if platform is None: 187 | export(ClockSignal("slow"), "slow_clk") 188 | export(pc, "pc") 189 | export(instr, "instr") 190 | export(isALUreg, "isALUreg") 191 | export(isALUimm, "isALUimm") 192 | export(isLoad, "isLoad") 193 | export(isStore, "isStore") 194 | export(isSystem, "isSystem") 195 | export(rdId, "rdId") 196 | export(rs1Id, "rs1Id") 197 | export(rs2Id, "rs2Id") 198 | export(Iimm, "Iimm") 199 | export(funct3, "funct3") 200 | export(rdId, "rdId") 201 | export(rs1, "rs1") 202 | export(rs2, "rs2") 203 | export(writeBackData, "writeBackData") 204 | export(writeBackEn, "writeBackEn") 205 | export(aluOut, "aluOut") 206 | export((1 << fsm.state), "state") 207 | 208 | return m 209 | -------------------------------------------------------------------------------- /15_load/cpu.py: -------------------------------------------------------------------------------- 1 | from amaranth import * 2 | 3 | class CPU(Elaboratable): 4 | 5 | def __init__(self): 6 | self.mem_addr = Signal(32) 7 | self.mem_rstrb = Signal() 8 | self.mem_rdata = Signal(32) 9 | self.x10 = Signal(32) 10 | self.fsm = None 11 | 12 | def elaborate(self, platform): 13 | m = Module() 14 | 15 | # Program counter 16 | pc = Signal(32) 17 | self.pc = pc 18 | 19 | # Memory 20 | mem_rdata = self.mem_rdata 21 | 22 | # Current instruction 23 | instr = Signal(32, reset=0b0110011) 24 | self.instr = instr 25 | 26 | # Register bank 27 | regs = Array([Signal(32, name="x"+str(x)) for x in range(32)]) 28 | rs1 = Signal(32) 29 | rs2 = Signal(32) 30 | 31 | # ALU registers 32 | aluOut = Signal(32) 33 | takeBranch = Signal(32) 34 | 35 | # Opcode decoder 36 | # It is nice to have these as actual signals for simulation 37 | isALUreg = Signal() 38 | isALUimm = Signal() 39 | isBranch = Signal() 40 | isJALR = Signal() 41 | isJAL = Signal() 42 | isAUIPC = Signal() 43 | isLUI = Signal() 44 | isLoad = Signal() 45 | isStore = Signal() 46 | isSystem = Signal() 47 | m.d.comb += [ 48 | isALUreg.eq(instr[0:7] == 0b0110011), 49 | isALUimm.eq(instr[0:7] == 0b0010011), 50 | isBranch.eq(instr[0:7] == 0b1100011), 51 | isJALR.eq(instr[0:7] == 0b1100111), 52 | isJAL.eq(instr[0:7] == 0b1101111), 53 | isAUIPC.eq(instr[0:7] == 0b0010111), 54 | isLUI.eq(instr[0:7] == 0b0110111), 55 | isLoad.eq(instr[0:7] == 0b0000011), 56 | isStore.eq(instr[0:7] == 0b0100011), 57 | isSystem.eq(instr[0:7] == 0b1110011) 58 | ] 59 | self.isALUreg = isALUreg 60 | self.isALUimm = isALUimm 61 | self.isBranch = isBranch 62 | self.isLoad = isLoad 63 | self.isStore = isStore 64 | self.isSystem = isSystem 65 | 66 | # Extend a signal with a sign bit repeated n times 67 | def SignExtend(signal, sign, n): 68 | return Cat(signal, sign.replicate(n)) 69 | 70 | # Immediate format decoder 71 | Uimm = Cat(Const(0).replicate(12), instr[12:32]) 72 | Iimm = Cat(instr[20:31], instr[31].replicate(21)) 73 | Simm = Cat(instr[7:12], instr[25:31], instr[31].replicate(21)) 74 | Bimm = Cat(0, instr[8:12], instr[25:31], instr[7], 75 | instr[31].replicate(20)) 76 | Jimm = Cat(0, instr[21:31], instr[20], instr[12:20], 77 | instr[31].replicate(12)) 78 | self.Iimm = Iimm 79 | 80 | # Register addresses decoder 81 | rs1Id = instr[15:20] 82 | rs2Id = instr[20:25] 83 | rdId = instr[7:12] 84 | 85 | self.rdId = rdId 86 | self.rs1Id = rs1Id 87 | self.rs2Id = rs2Id 88 | 89 | # Function code decdore 90 | funct3 = instr[12:15] 91 | funct7 = instr[25:32] 92 | self.funct3 = funct3 93 | 94 | # ALU 95 | aluIn1 = Signal.like(rs1) 96 | aluIn2 = Signal.like(rs2) 97 | shamt = Signal(5) 98 | aluMinus = Signal(33) 99 | aluPlus = Signal.like(aluIn1) 100 | 101 | m.d.comb += [ 102 | aluIn1.eq(rs1), 103 | aluIn2.eq(Mux((isALUreg | isBranch), rs2, Iimm)), 104 | shamt.eq(Mux(isALUreg, rs2[0:5], instr[20:25])) 105 | ] 106 | 107 | m.d.comb += [ 108 | aluMinus.eq(Cat(~aluIn1, C(0,1)) + Cat(aluIn2, C(0,1)) + 1), 109 | aluPlus.eq(aluIn1 + aluIn2) 110 | ] 111 | 112 | EQ = aluMinus[0:32] == 0 113 | LTU = aluMinus[32] 114 | LT = Mux((aluIn1[31] ^ aluIn2[31]), aluIn1[31], aluMinus[32]) 115 | 116 | def flip32(x): 117 | a = [x[i] for i in range(0, 32)] 118 | return Cat(*reversed(a)) 119 | 120 | # TODO: check these again! 121 | shifter_in = Mux(funct3 == 0b001, flip32(aluIn1), aluIn1) 122 | shifter = Cat(shifter_in, (instr[30] & aluIn1[31])) >> aluIn2[0:5] 123 | leftshift = flip32(shifter) 124 | 125 | with m.Switch(funct3) as alu: 126 | with m.Case(0b000): 127 | m.d.comb += aluOut.eq(Mux(funct7[5] & instr[5], 128 | aluMinus[0:32], aluPlus)) 129 | with m.Case(0b001): 130 | m.d.comb += aluOut.eq(leftshift) 131 | with m.Case(0b010): 132 | m.d.comb += aluOut.eq(LT) 133 | with m.Case(0b011): 134 | m.d.comb += aluOut.eq(LTU) 135 | with m.Case(0b100): 136 | m.d.comb += aluOut.eq(aluIn1 ^ aluIn2) 137 | with m.Case(0b101): 138 | m.d.comb += aluOut.eq(shifter) 139 | with m.Case(0b110): 140 | m.d.comb += aluOut.eq(aluIn1 | aluIn2) 141 | with m.Case(0b111): 142 | m.d.comb += aluOut.eq(aluIn1 & aluIn2) 143 | 144 | with m.Switch(funct3) as alu_branch: 145 | with m.Case(0b000): 146 | m.d.comb += takeBranch.eq(EQ) 147 | with m.Case(0b001): 148 | m.d.comb += takeBranch.eq(~EQ) 149 | with m.Case(0b100): 150 | m.d.comb += takeBranch.eq(LT) 151 | with m.Case(0b101): 152 | m.d.comb += takeBranch.eq(~LT) 153 | with m.Case(0b110): 154 | m.d.comb += takeBranch.eq(LTU) 155 | with m.Case(0b111): 156 | m.d.comb += takeBranch.eq(~LTU) 157 | with m.Case("---"): 158 | m.d.comb += takeBranch.eq(0) 159 | 160 | # Next program counter is either next intstruction or depends on 161 | # jump target 162 | pcPlusImm = pc + Mux(instr[3], Jimm[0:32], 163 | Mux(instr[4], Uimm[0:32], 164 | Bimm[0:32])) 165 | pcPlus4 = pc + 4 166 | 167 | nextPc = Mux(((isBranch & takeBranch) | isJAL), pcPlusImm, 168 | Mux(isJALR, Cat(C(0, 1), aluPlus[1:32]), 169 | pcPlus4)) 170 | 171 | # Main state machine 172 | with m.FSM(reset="FETCH_INSTR") as fsm: 173 | self.fsm = fsm 174 | with m.State("FETCH_INSTR"): 175 | m.next = "WAIT_INSTR" 176 | with m.State("WAIT_INSTR"): 177 | m.d.sync += instr.eq(self.mem_rdata) 178 | m.next = ("FETCH_REGS") 179 | with m.State("FETCH_REGS"): 180 | m.d.sync += [ 181 | rs1.eq(regs[rs1Id]), 182 | rs2.eq(regs[rs2Id]) 183 | ] 184 | m.next = "EXECUTE" 185 | with m.State("EXECUTE"): 186 | with m.If(~isSystem): 187 | m.d.sync += pc.eq(nextPc) 188 | with m.If(isLoad): 189 | m.next = "LOAD" 190 | with m.Else(): 191 | m.next = "FETCH_INSTR" 192 | with m.State("LOAD"): 193 | m.next = "WAIT_DATA" 194 | with m.State("WAIT_DATA"): 195 | m.next = "FETCH_INSTR" 196 | 197 | ## Load and store 198 | 199 | loadStoreAddr = Signal(32) 200 | m.d.comb += loadStoreAddr.eq(rs1 + Iimm) 201 | 202 | # Load 203 | memByteAccess = Signal() 204 | memHalfwordAccess = Signal() 205 | loadHalfword = Signal(16) 206 | loadByte = Signal(8) 207 | loadSign = Signal() 208 | loadData = Signal(32) 209 | 210 | m.d.comb += [ 211 | memByteAccess.eq(funct3[0:2] == C(0,2)), 212 | memHalfwordAccess.eq(funct3[0:2] == C(1,2)), 213 | loadHalfword.eq(Mux(loadStoreAddr[1], mem_rdata[16:32], 214 | mem_rdata[0:16])), 215 | loadByte.eq(Mux(loadStoreAddr[0], loadHalfword[8:16], 216 | loadHalfword[0:8])), 217 | loadSign.eq(~funct3[2] & Mux(memByteAccess, loadByte[7], 218 | loadHalfword[15])), 219 | loadData.eq( 220 | Mux(memByteAccess, SignExtend(loadByte, loadSign, 24), 221 | Mux(memHalfwordAccess, SignExtend(loadHalfword, 222 | loadSign, 16), 223 | mem_rdata))) 224 | ] 225 | 226 | # Wire memory address to pc or loadStoreAddr 227 | m.d.comb += [ 228 | self.mem_addr.eq( 229 | Mux(fsm.ongoing("WAIT_INSTR") | fsm.ongoing("FETCH_INSTR"), 230 | pc, loadStoreAddr)), 231 | self.mem_rstrb.eq(fsm.ongoing("FETCH_INSTR") | fsm.ongoing("LOAD")) 232 | ] 233 | 234 | 235 | # Register write back 236 | writeBackData = Mux((isJAL | isJALR), pcPlus4, 237 | Mux(isLUI, Uimm, 238 | Mux(isAUIPC, pcPlusImm, 239 | Mux(isLoad, loadData, 240 | aluOut)))) 241 | 242 | writeBackEn = ((fsm.ongoing("EXECUTE") & ~isBranch & ~isStore & ~isLoad) 243 | | fsm.ongoing("WAIT_DATA")) 244 | 245 | self.writeBackData = writeBackData 246 | 247 | 248 | with m.If(writeBackEn & (rdId != 0)): 249 | m.d.sync += regs[rdId].eq(writeBackData) 250 | # Also assign to debug output to see what is happening 251 | with m.If(rdId == 10): 252 | m.d.sync += self.x10.eq(writeBackData) 253 | 254 | return m 255 | --------------------------------------------------------------------------------