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,
o_DATA=self.data,
)
read_data = self.data.eq(self.memory._array[self.addr])
if self.synchronous and not self.transparent:
# 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)
elif self.synchronous:
# Synchronous, write-through port
# 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
# close to the correct behavior of a transparent port, and the difference should only
# be observable in pathological cases of clock gating.
f.add_statements(Switch(ClockSignal(self.domain),
{ 1: self.data.eq(self.data), 0: read_data }))
# be observable in pathological cases of clock gating. A register is injected to
# the address input to achieve the correct address-to-data latency. Also, the reset
# 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)
else:
# Asynchronous port
f.add_statements(read_data)
f.add_statements(self.data.eq(self.memory._array[self.addr]))
f.add_driver(self.data)
return f

View file

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