diff --git a/gateware/.gitignore b/gateware/.gitignore index 9d68e3c..aeca8e8 100644 --- a/gateware/.gitignore +++ b/gateware/.gitignore @@ -1,2 +1,7 @@ build/ -*.json \ No newline at end of file +*.json +__pycache__ + +# Sim artifacts +*.vcd +*.gtkw \ No newline at end of file diff --git a/gateware/requirements.txt b/gateware/requirements.txt new file mode 100644 index 0000000..9177aa9 --- /dev/null +++ b/gateware/requirements.txt @@ -0,0 +1,3 @@ +git+https://github.com/amaranth-lang/amaranth +git+https://github.com/amaranth-lang/amaranth-soc +git+https://github.com/minerva-cpu/minerva diff --git a/gateware/soc.py b/gateware/soc.py index d53bf5d..88ead03 100644 --- a/gateware/soc.py +++ b/gateware/soc.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from amaranth import * +from amaranth.sim import * from amaranth_boards import colorlight_i9 from amaranth_soc.wishbone import Interface, Arbiter, Decoder from amaranth_soc.memory import MemoryMap @@ -33,18 +34,18 @@ class Blinky(Elaboratable): class ROM(Elaboratable, Interface): def __init__(self, data=None): #self.size = len(data) - self.data = Memory(width=32, depth=4096, init=data) + self.data = Memory(width=32, depth=(4096 >> 2), init=data) self.r = self.data.read_port() # Need to init Interface - Interface.__init__(self, addr_width=12, data_width=32) + Interface.__init__(self, addr_width=10, data_width=32) # This is effectively a "window", and it has a certain set of resources # 12 = log2(4096) - memory_map = MemoryMap(addr_width=12, data_width=32) + memory_map = MemoryMap(addr_width=10, data_width=32) # TODO need to unify how I deal with size # In this case, one resource, which is out memory - memory_map.add_resource(self.data, name="rom_data", size=4096) + memory_map.add_resource(self.data, name="rom_data", size=(4096 >> 2)) self.memory_map = memory_map @@ -72,22 +73,24 @@ class ROM(Elaboratable, Interface): # End of simulated memory module. return m +# TODO support read segmentation or whatever it's called, where you read/write certian bytes from memory +# Otherwise we can't store individual bytes, and this will wreck shit in weird ways. class RAM(Elaboratable, Interface): def __init__(self): #self.size = len(data) - self.data = Memory(width=32, depth=4096) + self.data = Memory(width=32, depth=(4096 >> 2)) self.r = self.data.read_port() self.w = self.data.write_port() # Need to init Interface - Interface.__init__(self, addr_width=12, data_width=32) + Interface.__init__(self, addr_width=10, data_width=32) # This is effectively a "window", and it has a certain set of resources # 12 = log2(4096) - memory_map = MemoryMap(addr_width=12, data_width=32) + memory_map = MemoryMap(addr_width=10, data_width=32) # TODO need to unify how I deal with size # In this case, one resource, which is out memory - memory_map.add_resource(self.data, name="ram_data", size=4096) + memory_map.add_resource(self.data, name="ram_data", size=(4096 >> 2)) self.memory_map = memory_map @@ -155,7 +158,7 @@ class LEDPeripheral(Elaboratable, Interface): def load_firmware_for_mem() -> List[int]: - with open('../firmware/hello_world_c/hello_world.bin', 'rb') as f: + with open('../firmware/fw.bin', 'rb') as f: # Stored as little endian, LSB first?? data = f.read() out = [] @@ -165,12 +168,13 @@ def load_firmware_for_mem() -> List[int]: return out -class SoC(Elaboratable): - def __init__(self): +class Core(Elaboratable): + def __init__(self, led_signal): self.count = Signal(64) - self.cpu = Minerva() + self.cpu = Minerva(reset_address=0x01000000) self.arbiter = Arbiter(addr_width=32, data_width=32) self.decoder = Decoder(addr_width=32, data_width=32) + self.led_signal = led_signal def elaborate(self, platform): m = Module() @@ -179,12 +183,14 @@ class SoC(Elaboratable): m.submodules += self.decoder # Connect ibus and dbus together for simplicity for now - self.ibus = Interface(addr_width=32, data_width=32) - self.ibus.connect(self.cpu.ibus) + minerva_wb_features = ["cti", "bte", "err"] + self.ibus = Interface(addr_width=32, data_width=32, features=minerva_wb_features) + # Note this is a set of statements! without assigning to the comb domain, this will do nothing + m.d.comb += self.cpu.ibus.connect(self.ibus) self.arbiter.add(self.ibus) - self.dbus = Interface(addr_width=32, data_width=32) - self.dbus.connect(self.cpu.dbus) # Don't use .eq, use .connect, which will appropriately assign signals + self.dbus = Interface(addr_width=32, data_width=32, features=minerva_wb_features) + m.d.comb += self.cpu.dbus.connect(self.dbus) # Don't use .eq, use .connect, which will appropriately assign signals # using .eq() gave me Multiple Driven errors self.arbiter.add(self.dbus) @@ -210,7 +216,9 @@ class SoC(Elaboratable): # Hook up memory space self.rom = ROM(fw) m.submodules += self.rom - start, _stop, _step = self.decoder.add(self.rom, addr=0x01000000) + # Problem: not sure to handle how we do byte vs word addressing properly + # So doing this shift is a bit of a hacky way to impl anything + start, _stop, _step = self.decoder.add(self.rom, addr=(0x01000000 >> 2)) print(f"ROM added at 0x{start:08x}") self.ram = RAM() @@ -218,14 +226,13 @@ class SoC(Elaboratable): start, _stop, _step = self.decoder.add(self.ram) print(f"RAM added at 0x{start:08x}") - led_signal = platform.request("led") - self.led = LEDPeripheral(led_signal) + self.led = LEDPeripheral(self.led_signal) m.submodules += self.led start, _stop, _step = self.decoder.add(self.led) print(f"LED added at 0x{start:08x}") # Connect arbiter to decoder - self.arbiter.bus.connect(self.decoder.bus) + m.d.comb += self.arbiter.bus.connect(self.decoder.bus) # Counter #m.d.sync += self.count.eq(self.count + 1) @@ -235,5 +242,49 @@ class SoC(Elaboratable): return m +class SoC(Elaboratable): + def __init__(self): + pass + + def elaborate(self, platform): + m = Module() + + led_signal = platform.request("led") + core = Core(led_signal) + m.submodules += core + + return m + + +class TestDevice(Elaboratable): + def __init__(self): + pass + + + def elaborate(self, platform): + m = Module() + led_signal = Signal() + core = Core(led_signal) + m.submodules += core + + return m + +def run_sim(): + dut = TestDevice() + sim = Simulator(dut) + + def proc(): + for i in range(10000): + yield Tick() + + sim.add_clock(1e-6) + sim.add_sync_process(proc) + with sim.write_vcd('test.vcd', gtkw_file='test.gtkw'): + sim.reset() + sim.run() + + if __name__ == "__main__": - colorlight_i9.Colorlight_i9_Platform().build(SoC(), debug_verilog=True) \ No newline at end of file + colorlight_i9.Colorlight_i9_Platform().build(SoC(), debug_verilog=True) + + #run_sim() \ No newline at end of file