hdl.mem: ensure transparent read port model has correct latency.

This commit is contained in:
whitequark 2018-12-21 13:01:08 +00:00
parent 48d13e47ec
commit fa2af27bb0
2 changed files with 27 additions and 7 deletions

View file

@ -88,23 +88,38 @@ class ReadPort:
i_ADDR=self.addr, i_ADDR=self.addr,
o_DATA=self.data, o_DATA=self.data,
) )
read_data = self.data.eq(self.memory._array[self.addr])
if self.synchronous and not self.transparent: if self.synchronous and not self.transparent:
# Synchronous, read-before-write port # Synchronous, read-before-write port
f.add_statements(Switch(self.en, { 1: read_data })) f.add_statements(
Switch(self.en, {
1: self.data.eq(self.memory._array[self.addr])
})
)
f.add_driver(self.data, self.domain) f.add_driver(self.data, self.domain)
elif self.synchronous: elif self.synchronous:
# Synchronous, write-through port # Synchronous, write-through port
# This model is a bit unconventional. We model transparent ports as asynchronous ports # This model is a bit unconventional. We model transparent ports as asynchronous ports
# that are latched when the clock is high. This isn't exactly correct, but it is very # that are latched when the clock is high. This isn't exactly correct, but it is very
# close to the correct behavior of a transparent port, and the difference should only # close to the correct behavior of a transparent port, and the difference should only
# be observable in pathological cases of clock gating. # be observable in pathological cases of clock gating. A register is injected to
f.add_statements(Switch(ClockSignal(self.domain), # the address input to achieve the correct address-to-data latency. Also, the reset
{ 1: self.data.eq(self.data), 0: read_data })) # value of the data output is forcibly set to the 0th initial value, if any--note that
# many FPGAs do not guarantee this behavior!
if len(self.memory.init) > 0:
self.data.reset = self.memory.init[0]
latch_addr = Signal.like(self.addr)
f.add_statements(
latch_addr.eq(self.addr),
Switch(ClockSignal(self.domain), {
0: self.data.eq(self.data),
1: self.data.eq(self.memory._array[latch_addr]),
}),
)
f.add_driver(latch_addr, self.domain)
f.add_driver(self.data) f.add_driver(self.data)
else: else:
# Asynchronous port # Asynchronous port
f.add_statements(read_data) f.add_statements(self.data.eq(self.memory._array[self.addr]))
f.add_driver(self.data) f.add_driver(self.data)
return f return f

View file

@ -419,13 +419,14 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
self.setUp_memory() self.setUp_memory()
with self.assertSimulation(self.m) as sim: with self.assertSimulation(self.m) as sim:
def process(): def process():
yield
self.assertEqual((yield self.rdport.data), 0xaa) self.assertEqual((yield self.rdport.data), 0xaa)
yield self.rdport.addr.eq(1) yield self.rdport.addr.eq(1)
yield yield
yield
self.assertEqual((yield self.rdport.data), 0x55) self.assertEqual((yield self.rdport.data), 0x55)
yield self.rdport.addr.eq(2) yield self.rdport.addr.eq(2)
yield yield
yield
self.assertEqual((yield self.rdport.data), 0x00) self.assertEqual((yield self.rdport.data), 0x00)
sim.add_clock(1e-6) sim.add_clock(1e-6)
sim.add_sync_process(process) sim.add_sync_process(process)
@ -493,6 +494,10 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
self.assertEqual((yield self.rdport.data), 0xaa) self.assertEqual((yield self.rdport.data), 0xaa)
yield Delay(1e-6) # let comb propagate yield Delay(1e-6) # let comb propagate
self.assertEqual((yield self.rdport.data), 0x33) self.assertEqual((yield self.rdport.data), 0x33)
yield
yield self.rdport.addr.eq(1)
yield Delay(1e-6) # let comb propagate
self.assertEqual((yield self.rdport.data), 0x33)
sim.add_clock(1e-6) sim.add_clock(1e-6)
sim.add_sync_process(process) sim.add_sync_process(process)