from amaranth import * from amaranth_soc.wishbone import * from amaranth_soc.memory import * # TODO impl select # 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, granularity=8) # This is effectively a "window", and it has a certain set of resources # 12 = log2(4096) memory_map = MemoryMap(addr_width=12, data_width=8) # 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, granularity=8) # This is effectively a "window", and it has a certain set of resources # 12 = log2(4096) memory_map = MemoryMap(addr_width=12, data_width=8) # 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