gateware: fixed various bus issues
- The Record.connect() function returns statements that need to be added to the comb domain. - Addressing of the devices works on word-sized chunks, so everything needs to be adjusted there
This commit is contained in:
parent
1cfa5a47de
commit
883b712a53
5
gateware/.gitignore
vendored
5
gateware/.gitignore
vendored
@ -1,2 +1,7 @@
|
|||||||
build/
|
build/
|
||||||
*.json
|
*.json
|
||||||
|
__pycache__
|
||||||
|
|
||||||
|
# Sim artifacts
|
||||||
|
*.vcd
|
||||||
|
*.gtkw
|
3
gateware/requirements.txt
Normal file
3
gateware/requirements.txt
Normal file
@ -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
|
@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from amaranth import *
|
from amaranth import *
|
||||||
|
from amaranth.sim import *
|
||||||
from amaranth_boards import colorlight_i9
|
from amaranth_boards import colorlight_i9
|
||||||
from amaranth_soc.wishbone import Interface, Arbiter, Decoder
|
from amaranth_soc.wishbone import Interface, Arbiter, Decoder
|
||||||
from amaranth_soc.memory import MemoryMap
|
from amaranth_soc.memory import MemoryMap
|
||||||
@ -33,18 +34,18 @@ class Blinky(Elaboratable):
|
|||||||
class ROM(Elaboratable, Interface):
|
class ROM(Elaboratable, Interface):
|
||||||
def __init__(self, data=None):
|
def __init__(self, data=None):
|
||||||
#self.size = len(data)
|
#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()
|
self.r = self.data.read_port()
|
||||||
|
|
||||||
# Need to init Interface
|
# 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
|
# This is effectively a "window", and it has a certain set of resources
|
||||||
# 12 = log2(4096)
|
# 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
|
# TODO need to unify how I deal with size
|
||||||
# In this case, one resource, which is out memory
|
# 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
|
self.memory_map = memory_map
|
||||||
|
|
||||||
@ -72,22 +73,24 @@ class ROM(Elaboratable, Interface):
|
|||||||
# End of simulated memory module.
|
# End of simulated memory module.
|
||||||
return m
|
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):
|
class RAM(Elaboratable, Interface):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
#self.size = len(data)
|
#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.r = self.data.read_port()
|
||||||
self.w = self.data.write_port()
|
self.w = self.data.write_port()
|
||||||
|
|
||||||
# Need to init Interface
|
# 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
|
# This is effectively a "window", and it has a certain set of resources
|
||||||
# 12 = log2(4096)
|
# 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
|
# TODO need to unify how I deal with size
|
||||||
# In this case, one resource, which is out memory
|
# 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
|
self.memory_map = memory_map
|
||||||
|
|
||||||
@ -155,7 +158,7 @@ class LEDPeripheral(Elaboratable, Interface):
|
|||||||
|
|
||||||
|
|
||||||
def load_firmware_for_mem() -> List[int]:
|
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??
|
# Stored as little endian, LSB first??
|
||||||
data = f.read()
|
data = f.read()
|
||||||
out = []
|
out = []
|
||||||
@ -165,12 +168,13 @@ def load_firmware_for_mem() -> List[int]:
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
class SoC(Elaboratable):
|
class Core(Elaboratable):
|
||||||
def __init__(self):
|
def __init__(self, led_signal):
|
||||||
self.count = Signal(64)
|
self.count = Signal(64)
|
||||||
self.cpu = Minerva()
|
self.cpu = Minerva(reset_address=0x01000000)
|
||||||
self.arbiter = Arbiter(addr_width=32, data_width=32)
|
self.arbiter = Arbiter(addr_width=32, data_width=32)
|
||||||
self.decoder = Decoder(addr_width=32, data_width=32)
|
self.decoder = Decoder(addr_width=32, data_width=32)
|
||||||
|
self.led_signal = led_signal
|
||||||
|
|
||||||
def elaborate(self, platform):
|
def elaborate(self, platform):
|
||||||
m = Module()
|
m = Module()
|
||||||
@ -179,12 +183,14 @@ class SoC(Elaboratable):
|
|||||||
m.submodules += self.decoder
|
m.submodules += self.decoder
|
||||||
|
|
||||||
# Connect ibus and dbus together for simplicity for now
|
# Connect ibus and dbus together for simplicity for now
|
||||||
self.ibus = Interface(addr_width=32, data_width=32)
|
minerva_wb_features = ["cti", "bte", "err"]
|
||||||
self.ibus.connect(self.cpu.ibus)
|
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.arbiter.add(self.ibus)
|
||||||
|
|
||||||
self.dbus = Interface(addr_width=32, data_width=32)
|
self.dbus = Interface(addr_width=32, data_width=32, features=minerva_wb_features)
|
||||||
self.dbus.connect(self.cpu.dbus) # Don't use .eq, use .connect, which will appropriately assign signals
|
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
|
# using .eq() gave me Multiple Driven errors
|
||||||
self.arbiter.add(self.dbus)
|
self.arbiter.add(self.dbus)
|
||||||
|
|
||||||
@ -210,7 +216,9 @@ class SoC(Elaboratable):
|
|||||||
# Hook up memory space
|
# Hook up memory space
|
||||||
self.rom = ROM(fw)
|
self.rom = ROM(fw)
|
||||||
m.submodules += self.rom
|
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}")
|
print(f"ROM added at 0x{start:08x}")
|
||||||
|
|
||||||
self.ram = RAM()
|
self.ram = RAM()
|
||||||
@ -218,14 +226,13 @@ class SoC(Elaboratable):
|
|||||||
start, _stop, _step = self.decoder.add(self.ram)
|
start, _stop, _step = self.decoder.add(self.ram)
|
||||||
print(f"RAM added at 0x{start:08x}")
|
print(f"RAM added at 0x{start:08x}")
|
||||||
|
|
||||||
led_signal = platform.request("led")
|
self.led = LEDPeripheral(self.led_signal)
|
||||||
self.led = LEDPeripheral(led_signal)
|
|
||||||
m.submodules += self.led
|
m.submodules += self.led
|
||||||
start, _stop, _step = self.decoder.add(self.led)
|
start, _stop, _step = self.decoder.add(self.led)
|
||||||
print(f"LED added at 0x{start:08x}")
|
print(f"LED added at 0x{start:08x}")
|
||||||
|
|
||||||
# Connect arbiter to decoder
|
# Connect arbiter to decoder
|
||||||
self.arbiter.bus.connect(self.decoder.bus)
|
m.d.comb += self.arbiter.bus.connect(self.decoder.bus)
|
||||||
|
|
||||||
# Counter
|
# Counter
|
||||||
#m.d.sync += self.count.eq(self.count + 1)
|
#m.d.sync += self.count.eq(self.count + 1)
|
||||||
@ -235,5 +242,49 @@ class SoC(Elaboratable):
|
|||||||
|
|
||||||
return m
|
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__":
|
if __name__ == "__main__":
|
||||||
colorlight_i9.Colorlight_i9_Platform().build(SoC(), debug_verilog=True)
|
colorlight_i9.Colorlight_i9_Platform().build(SoC(), debug_verilog=True)
|
||||||
|
|
||||||
|
#run_sim()
|
Loading…
Reference in New Issue
Block a user