Adding latency in strategic places.? - Page 1
Forum section: electronics / general

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 02:15:21 pm
content: Project: Simulated Z80. Simulated ROM and RAM. Broken out module for "glue_logic" to do address decoding and gating the ROM and RAM.Bug: The truth tables for the memory CE and OE don't appear to work when the system is running.Issue found:The Z80 datasheet is bending the truth a little when it's "textual description" of events and their ordering.In particular it says:"Roughly half a clock cycle later MREQ will go active. When active, RD will ....."Yet in the timing diagram they show RD actually falls slightly behind MREQ.I was triggering the ROM "read" on the falling edge of RD->OE and checking the state of MREQ->CE.According to the datasheet that should still work, but in the Z80 simulator I have it appears they all fall simultaneously.I flipped it round to follow the "Natural language" of the datasheet, and trigger effectively on the falling edge of MREQ and check the state of RD at that instant. This works. However I am too long in the tooth for that too fool me here. It works, today.What is the best way to cause delays, can I just sleep (2ns) in a always block? I figure that is not the correct way, so what would be? Biggest win, least effort?The CPU lines are attached via "wire" and "assign" blocks,o_drive elsewhere 'latches' the output_buffer register onto the wire data bus.Code:// Readalways @(negedge nCE) beginif (!nOE) beginoutput_buffer <= rom_data;o_drive =1;end else beginoutput_buffer <= 8'bz;o_drive =0;endend// Readalways @(posedge nCE) beginoutput_buffer <= 8'bz;o_drive =0;endNote: None of this really matters in the real world with actual 1970s logic chips, it's just a simulation artifact.EDIT: If I drop the simulation resolution from 1ps to 1ns would that make this better or worse?

author: tggzzz
author_rank: Super Contributor
timestamp: May 30, 2025, 02:48:02 pm
content: What do other manufacturers' data sheets say?What timing information is specified in the datasheet? (Seconds, not adjectives)What causality is specified in the waveform diagrams in the datasheet?What makes you think the simulator is "correct", which implies defining "correct/incorrect"?

author: BrianHG
author_rank: Super Contributor
timestamp: May 30, 2025, 02:56:09 pm
content: I do not know if this will help, but when I was working on the Z80 bus interface for Nockieboy's video card project, I created a true full Z80 model timing accurate test-bench for any free version of modelsim.It allows you to script Z80 read/write, instruction fetch and portIO access while interfacing with your own logic. I've color coded the access timing in the output weaveform to show you where and when you need to hold your data according to the Z80 datasheet.Or, just use my synchronous Z80 bus interface code included which only requires a core FPGA clock of above >=80MHz and you can do any core timed fpga Z80 linked verilog and let my core worry about the timing, bus holds & optional wait states.See here:Woops, I never uploaded it to my github. Ok, I attached a .zip of the project.In modelsim, all you need to do is:Change your working directory to the unzipped folder: Z80_Interface_TB_v16Then:do setup_z80.doThen:do run_z80.doMy Z80 bus model will run the ascii script:Z80_cmd_stimulus.txt (instructions inside...)Do not run the DDR3 stuff. It only works on Altera's free modelsim and only serves to show you an external Z80 wired to an FPGA as a DDR3 ram controller.For your own work, you would replace the 'Z80_Bridge.sv' as the top of the testbench code.You will also need to edit the 'setup_z80.do' with your own as a lot of the original GPU interface signals, part of the Z80_Bridge' will no longer be part of the displayed waveform.

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 30, 2025, 03:26:39 pm
content: Preferably implement it as a synchronous state machine internally. Delays will then just be a matter of counting internal clock pulses, simple and accurate. Use a significantly higher internal clock freq than the CPU clock.Implement proper clock domain crossing for all inputs.Purely asynchronous designs in a FPGA are rarely a good approach.

author: tggzzz
author_rank: Super Contributor
timestamp: May 30, 2025, 03:28:36 pm
content: Quote from: SiliconWizard on May 30, 2025, 03:26:39 pmPrecisely, especially since "rarely" does not exclude extraordinary cases.

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 03:34:03 pm
content: Quote from: tggzzz on May 30, 2025, 02:48:02 pmI don't know.QuoteThe delay in question is 13-8 which only specifies the maximum delays in tens of ns. On the 20Mhz model both 8 and 13 are 40ns. That does not mean simultaneous. It just means "dont look that quickly"?QuoteFalling Clock edge -> Falling signal line after "delay".QuoteNothing. I actually expect it's wrong and simultaneous, all of the lines seem to change in a single pico second.My question, as a verilog beginner, was ... are there easy ways to cause a edge trigger delay. I mean I can do it in a testbench. All I want to have happen is the trigger "skip a simulation tick" to allow all transitions to have occured before sampling them.As it turns out, for simulation purposes only, it seemsCode:#0;Allow me to trigger on either MREQ or RD and the other state will have transitioned.

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 03:39:46 pm
content: Quote from: SiliconWizard on May 30, 2025, 03:26:39 pmI was hoping to keep it simple. Its just that it all runs too fast. I need a way to slow the ROM and RAM down, at least my glue layer, the thing I am using it to simulate.I don't need to get it perfect, far from it. I just want to spend more time building the address decoder/bus mastering for ROM/RAM/IO/DMA without tripping over "causality" issues and lorrenz transformations.

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 03:47:29 pm
content: Contradicting myself here... the secondary process here is learning verilog. So ...If I understand what you mean by making the memory (and address logic) async.. I would use the flip-flops and an internal clock to "clock in" the signal lines as EACH changes. Then those cascade, such that if any transitions the state is reassessed and the output states set. More flip flops then "clock" the copy to the output buffer and then the "latching". Instead of doing it all at once and hoping everything lands exactly in place, I can spread it out over 4 or 8 "TBD ns" clock pulses and make the timing more reliable.I probably explained that incorrectly.

author: tggzzz
author_rank: Super Contributor
timestamp: May 30, 2025, 04:15:57 pm
content: Quote from: paulca on May 30, 2025, 03:34:03 pmIt could be 0ns, i.e. simultaneous and meet the datasheet spec.It does not mean "don't look that quickly". The specified maximum is to enable you to design a circuit where the setup times of external circuits are observed.QuoteYou should design your external circuit such that it doesn't matter which one transitions first. Synchronous (i.e. clocked) circuits allow for exactly that - all that matters is that all transitions are finished (in any order) before the register's tsetup, and there are no new transitions until after thold

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 04:32:02 pm
content: Quote from: paulca on May 30, 2025, 03:34:03 pmThis turns out to be a poison apple. Taboo. That it did anything is an anomoly and probably because I am running this without synthesis.

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 04:38:25 pm
content: This is the bit that matters in terms of "output circuit". The rest is basically simulation to validate this as it grows more complex.It is going to be hardware 74HC gates for a long while. They are not clocked.So I am assume this bit is "fine", but to properly uncouple myself from the pico-second domain I need to clock the simulated RAM and ROM and any peripherals to not be simultaneous?Code:module glue_logic (input wire addr_15,input wire nRD,input wire nWR,input wire nMREQ,output wire nROM_CE,output wire nROM_OE,output wire nRAM_CE,output wire nRAM_OE,output wire nRAM_WE);assign nROM_CE = nMREQ | addr_15;assign nRAM_CE = nMREQ | ~addr_15;assign nROM_OE = nRD;assign nRAM_OE = nRD;assign nRAM_WE = nWR;endmodule// R A15 ROMCE// 0 0 0// 0 1 1// 1 0 1// 1 1 1// R A15 RAMCE// 0 0 1// 0 1 0// 1 0 1// 1 1 1

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 05:37:37 pm
content: ROM with internal clock. It works - defined as a test program in that "ROM" runs. But I don't have time to properly test its doing anything useful regards timing.The Z80 clk has been slowed to a 'real' 1Mhz.The new "sysclk" is generated at 50Mhz.Code:// rom_async.vmodule rom_async (input wire [14:0] addr,input wire nCE,input wire nOE,inout wire [7:0] data,input wire clk);reg _nCE;reg _nOE;reg [7:0] rom_data [0:1023]; // 32kreg [7:0] output_buffer;reg [14:0] address_buffer;reg o_drive;initial beginoutput_buffer = 8'bz;o_drive = 0;_nCE = 1;_nOE = 1;$readmemh("asm/test.hex", rom_data);endalways @(posedge clk) begin_nCE <= nCE;_nOE <= nOE;address_buffer <= addr;endalways @(posedge clk) beginif(~_nOE & ~_nCE ) beginoutput_buffer <= rom_data;o_drive <= 1;end else begino_drive <= 0;endendassign data = (o_drive) ? output_buffer : 8'bz;endmoduleEDIT: Tests show is it delayed and clocked to sysclk.. CE/OE are not 'checked' the same sysclk edge they transition.EDIT: Lessons learnt. In the output (GTKwave). "x" can mean "conflicted" or "invalid". So when you forget to actually load any data into the ROM, ALL addresses will hold "xx". So check that before you spend an hour debugging it as a conflict. The ROM is NOT broken, its un-initialized.

author: BrianHG
author_rank: Super Contributor
timestamp: May 30, 2025, 05:52:32 pm
content: Quote from: paulca on May 30, 2025, 05:37:37 pmYou realize that stripping out my Z80_bridge from my code provided above, placing your sync code in above. And just set in my parameters in my testbench to your desired Z80-[speed version], its clock frequency and your codes clock frequency. My testbench will run my ascii test script where you can tell my dummy Z80 to perform a bus read or write, let your code respond, and look at the waveform + result log to see if a real Z80 will accept your results.My Z80 testbench basically emulates all Z80 bus cycles for you [the ones you place in the ascii script file] and shows you where your interface code's timing messed up.

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 06:01:53 pm
content: Quote from: BrianHG on May 30, 2025, 05:52:32 pmThank you, but I like the Z80 I have as it's opaque when I want it to be.Code:run:sjasmplus --raw=asm/test.bin asm/test.asmxxd -p -c 1 asm/test.bin > asm/test.hexiverilog -o z80_test *.vvvp z80_testgtkwave dump.vcd dump.gtkwSingle button dev loop from actual z80 ASM in VSCode to a GTKWave trace.Mostly it's about "momentum induced ignorance". This rabbit holes gravity is strong.If done right I also get to use it to write and test ROM routines with simulated hardware. At the moment it's 3 ICs+Glue. That will change quickly. "This" entire endeavour is 50% making a test bench for that process and 50% learning FPGA.

author: paulca
author_rank: Super Contributor
timestamp: May 30, 2025, 07:06:55 pm
content: A testament to "software compatibility" I took a random Z80 tutorial that produces an LED tracer on the data bus.With the only modification to comment out the delay call...It runs. I can see / filter on the IOREQ line in the dump file and see the 1, 2, 4, 8, 16It also shows, as an accidental test, that during that IOREQ the bus is NOT contended by the dodgy ROM impl.... epic.