From fa2af27bb0d0767107e7ed68f79e4fa767460d06 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 21 Dec 2018 13:01:08 +0000 Subject: [PATCH] hdl.mem: ensure transparent read port model has correct latency. --- nmigen/hdl/mem.py | 27 +++++++++++++++++++++------ nmigen/test/test_sim.py | 7 ++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/nmigen/hdl/mem.py b/nmigen/hdl/mem.py index 99ed6e1..3467a2b 100644 --- a/nmigen/hdl/mem.py +++ b/nmigen/hdl/mem.py @@ -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 diff --git a/nmigen/test/test_sim.py b/nmigen/test/test_sim.py index 070fcdb..4dd7b5b 100644 --- a/nmigen/test/test_sim.py +++ b/nmigen/test/test_sim.py @@ -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) -- 2.30.2