gateware: start spreading out into a couple modules
This commit is contained in:
parent
296206524c
commit
5619482029
34
gateware/led.py
Normal file
34
gateware/led.py
Normal file
@ -0,0 +1,34 @@
|
||||
from amaranth import *
|
||||
from amaranth_soc.wishbone import *
|
||||
|
||||
|
||||
class LEDPeripheral(Elaboratable, Interface):
|
||||
def __init__(self, led_signal):
|
||||
Interface.__init__(self, addr_width=1, data_width=32)
|
||||
memory_map = MemoryMap(addr_width=1, data_width=32)
|
||||
#memory_map.add_resource("my_led", name="led_peripheral", size=1)
|
||||
self.memory_map = memory_map
|
||||
|
||||
self.led = led_signal
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
storage = Signal(1)
|
||||
|
||||
# Always update read values (both wishbone and the LED outpu)
|
||||
m.d.comb += [
|
||||
self.dat_r[0].eq(storage),
|
||||
self.led.eq(storage),
|
||||
]
|
||||
|
||||
m.d.sync += self.ack.eq(0) # default to no ack
|
||||
with m.If(self.cyc & self.stb):
|
||||
# single cycle ack when CYC and STB are asserted
|
||||
m.d.sync += self.ack.eq(1)
|
||||
|
||||
# Write to our storage register if the value has changed
|
||||
with m.If(self.we):
|
||||
m.d.sync += storage.eq(self.dat_w[0])
|
||||
|
||||
return m
|
173
gateware/main.py
173
gateware/main.py
@ -3,160 +3,17 @@
|
||||
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
|
||||
from amaranth_soc.wishbone import *
|
||||
|
||||
from minerva.core import Minerva
|
||||
|
||||
from typing import List
|
||||
from argparse import ArgumentParser
|
||||
|
||||
class Blinky(Elaboratable):
|
||||
def __init__(self):
|
||||
self.count = Signal(64)
|
||||
|
||||
def elaborate(self, platform):
|
||||
led = platform.request("led")
|
||||
|
||||
m = Module()
|
||||
|
||||
# Counter
|
||||
m.d.sync += self.count.eq(self.count + 1)
|
||||
with m.If(self.count >= 50000000):
|
||||
m.d.sync += self.count.eq(0)
|
||||
m.d.sync += led.eq(~led)
|
||||
|
||||
return m
|
||||
|
||||
# To change clock domain of a module:
|
||||
# new_thing = DomainRenamer("new_clock")(MyElaboratable())
|
||||
|
||||
# We sub-class wishbone.Interface here because it needs to be a bus object to be added as a window to Wishbone stuff
|
||||
class ROM(Elaboratable, Interface):
|
||||
def __init__(self, data=None):
|
||||
#self.size = len(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=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=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 >> 2))
|
||||
|
||||
self.memory_map = memory_map
|
||||
|
||||
# Connects memory port signals to wishbone interface
|
||||
def elaborate(self, platform):
|
||||
# Stolen from https://vivonomicon.com/2020/04/14/learning-fpga-design-with-nmigen/
|
||||
m = Module()
|
||||
# Register the read port submodule.
|
||||
m.submodules.r = self.r
|
||||
|
||||
# 'ack' signal should rest at 0.
|
||||
m.d.sync += self.ack.eq( 0 )
|
||||
# Simulated reads only take one cycle, but only acknowledge
|
||||
# them after 'cyc' and 'stb' are asserted.
|
||||
with m.If( self.cyc ):
|
||||
m.d.sync += self.ack.eq( self.stb )
|
||||
|
||||
# Set 'dat_r' bus signal to the value in the
|
||||
# requested 'data' array index.
|
||||
m.d.comb += [
|
||||
self.r.addr.eq( self.adr ),
|
||||
self.dat_r.eq( self.r.data )
|
||||
]
|
||||
|
||||
# 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 >> 2))
|
||||
self.r = self.data.read_port()
|
||||
self.w = self.data.write_port()
|
||||
|
||||
# Need to init Interface
|
||||
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=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 >> 2))
|
||||
|
||||
self.memory_map = memory_map
|
||||
|
||||
# Connects memory port signals to wishbone interface
|
||||
def elaborate(self, platform):
|
||||
# Stolen from https://vivonomicon.com/2020/04/14/learning-fpga-design-with-nmigen/
|
||||
m = Module()
|
||||
# Register the read port submodule.
|
||||
m.submodules.r = self.r
|
||||
m.submodules.w = self.w
|
||||
|
||||
# 'ack' signal should rest at 0.
|
||||
m.d.sync += self.ack.eq(0)
|
||||
# Simulated reads only take one cycle, but only acknowledge
|
||||
# them after 'cyc' and 'stb' are asserted.
|
||||
with m.If( self.cyc & self.stb):
|
||||
m.d.sync += self.ack.eq(1)
|
||||
|
||||
# Write to address if we are writing
|
||||
with m.If(self.we):
|
||||
m.d.sync += self.w.en.eq(1)
|
||||
|
||||
# Set 'dat_r' bus signal to the value in the
|
||||
# requested 'data' array index.
|
||||
m.d.comb += [
|
||||
self.r.addr.eq( self.adr ),
|
||||
self.dat_r.eq( self.r.data ),
|
||||
self.w.addr.eq(self.adr),
|
||||
self.w.data.eq(self.dat_w),
|
||||
]
|
||||
|
||||
# End of simulated memory module.
|
||||
return m
|
||||
|
||||
class LEDPeripheral(Elaboratable, Interface):
|
||||
def __init__(self, led_signal):
|
||||
Interface.__init__(self, addr_width=1, data_width=32)
|
||||
memory_map = MemoryMap(addr_width=1, data_width=32)
|
||||
#memory_map.add_resource("my_led", name="led_peripheral", size=1)
|
||||
self.memory_map = memory_map
|
||||
|
||||
self.led = led_signal
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
storage = Signal(1)
|
||||
|
||||
# Always update read values (both wishbone and the LED outpu)
|
||||
m.d.comb += [
|
||||
self.dat_r[0].eq(storage),
|
||||
self.led.eq(storage),
|
||||
]
|
||||
|
||||
m.d.sync += self.ack.eq(0) # default to no ack
|
||||
with m.If(self.cyc & self.stb):
|
||||
# single cycle ack when CYC and STB are asserted
|
||||
m.d.sync += self.ack.eq(1)
|
||||
|
||||
# Write to our storage register if the value has changed
|
||||
with m.If(self.we):
|
||||
m.d.sync += storage.eq(self.dat_w[0])
|
||||
|
||||
return m
|
||||
|
||||
|
||||
# TODO clean this up, generate binary here, maybe even run cargo build
|
||||
def load_firmware_for_mem() -> List[int]:
|
||||
@ -247,32 +104,18 @@ class SoC(Elaboratable):
|
||||
pass
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
if platform is not None:
|
||||
led_signal = platform.request("led")
|
||||
else:
|
||||
# platform is None in simulation, so provide harnesses for required signals
|
||||
led_signal = Signal()
|
||||
|
||||
led_signal = platform.request("led")
|
||||
core = Core(led_signal)
|
||||
m.submodules.core = core
|
||||
return Core(led_signal)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
# TODO add more harnessing
|
||||
class TestDevice(Elaboratable):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
led_signal = Signal()
|
||||
core = Core(led_signal)
|
||||
m.submodules.core = core
|
||||
|
||||
return m
|
||||
|
||||
# TODO add structure to add regression tests
|
||||
def run_sim():
|
||||
dut = TestDevice()
|
||||
dut = SoC()
|
||||
sim = Simulator(dut)
|
||||
|
||||
def proc():
|
||||
|
100
gateware/memory.py
Normal file
100
gateware/memory.py
Normal file
@ -0,0 +1,100 @@
|
||||
from amaranth import *
|
||||
from amaranth_soc.wishbone import *
|
||||
from amaranth_soc.memory import *
|
||||
|
||||
|
||||
# We sub-class wishbone.Interface here because it needs to be a bus object to be added as a window to Wishbone stuff
|
||||
class ROM(Elaboratable, Interface):
|
||||
def __init__(self, data=None):
|
||||
#self.size = len(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=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=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 >> 2))
|
||||
|
||||
self.memory_map = memory_map
|
||||
|
||||
# Connects memory port signals to wishbone interface
|
||||
def elaborate(self, platform):
|
||||
# Stolen from https://vivonomicon.com/2020/04/14/learning-fpga-design-with-nmigen/
|
||||
m = Module()
|
||||
# Register the read port submodule.
|
||||
m.submodules.r = self.r
|
||||
|
||||
# 'ack' signal should rest at 0.
|
||||
m.d.sync += self.ack.eq( 0 )
|
||||
# Simulated reads only take one cycle, but only acknowledge
|
||||
# them after 'cyc' and 'stb' are asserted.
|
||||
with m.If( self.cyc ):
|
||||
m.d.sync += self.ack.eq( self.stb )
|
||||
|
||||
# Set 'dat_r' bus signal to the value in the
|
||||
# requested 'data' array index.
|
||||
m.d.comb += [
|
||||
self.r.addr.eq( self.adr ),
|
||||
self.dat_r.eq( self.r.data )
|
||||
]
|
||||
|
||||
# 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 >> 2))
|
||||
self.r = self.data.read_port()
|
||||
self.w = self.data.write_port()
|
||||
|
||||
# Need to init Interface
|
||||
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=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 >> 2))
|
||||
|
||||
self.memory_map = memory_map
|
||||
|
||||
# Connects memory port signals to wishbone interface
|
||||
def elaborate(self, platform):
|
||||
# Stolen from https://vivonomicon.com/2020/04/14/learning-fpga-design-with-nmigen/
|
||||
m = Module()
|
||||
# Register the read port submodule.
|
||||
m.submodules.r = self.r
|
||||
m.submodules.w = self.w
|
||||
|
||||
# 'ack' signal should rest at 0.
|
||||
m.d.sync += self.ack.eq(0)
|
||||
# Simulated reads only take one cycle, but only acknowledge
|
||||
# them after 'cyc' and 'stb' are asserted.
|
||||
with m.If( self.cyc & self.stb):
|
||||
m.d.sync += self.ack.eq(1)
|
||||
|
||||
# Write to address if we are writing
|
||||
with m.If(self.we):
|
||||
m.d.sync += self.w.en.eq(1)
|
||||
|
||||
# Set 'dat_r' bus signal to the value in the
|
||||
# requested 'data' array index.
|
||||
m.d.comb += [
|
||||
self.r.addr.eq( self.adr ),
|
||||
self.dat_r.eq( self.r.data ),
|
||||
self.w.addr.eq(self.adr),
|
||||
self.w.data.eq(self.dat_w),
|
||||
]
|
||||
|
||||
# End of simulated memory module.
|
||||
return m
|
Loading…
Reference in New Issue
Block a user