test07d
now implements:
- A simple ROM-like module for storing numeral images as 3×5 pixel patterns for the digits 0..9.
- A 4-digit demical counter using BCD.
- Internal frame counter that increments the BCD counter when each 60 frames ticks over, or increments it on every frame if the right button is pressed.
- Reset when the left button is pressed.
Some specific details:
- The left LED toggles every 60 frames.
- The right LED is illuminated while VBLANK is asserted. This works out to be 45÷525 or 8.6% of the time. Hence it is dim.
- Pressing the left button will reset all the counters.
- While the right button is held, the frame counter is ignored and the BCD counter updates on every VBLANK (i.e. on every frame).
- One frame lasts 420,000 clock cycles (800×525), which at 25MHz is 59.5238Hz. However, we can see that the monitor believes the frame rate is 59.50Hz, or 99.96%.
- My phone has counted about 2240.8 seconds, while the VGA generator has counted 2223×60 frames. So, 2223×60÷59.5238 = 2240.78, which is pretty much spot on for a 25MHz clock. Nice one.
- It's likely we could update the BCD counter using an actual divide-by-50-million counter, if we wanted to, and then have a separate buffer/latch for the value of the digits that latches on VBLANK, to display an exact number of seconds.
- I wonder if we could count 521 lines instead of 525? That would be a frame rate of 59.9808Hz.
- We can't get exactly 60Hz out of 25Mhz, because that would be 416,666.666 "pixels". However, to get close to 416,667:
- ÷792 = 526.095, or 526×792 = 416,592 => 60.011Hz (100.01792%), or 1 second gained every 2 hours.
- ÷801 = 520.184, or 520×801 = 416,520 => 60.021Hz (100.03521%), or 1 second gained every hour.
- ÷806 = 516.957, or 517×806 = 416,702 => 59.995Hz (99.99152%), or 1 second lost every 3 hours.
- Alternatively, 800×520+667=416,667 but I don't know if a monitor would like that, and it would mean HSYNC doesn't align perfectly...?
- With this design, I was hitting some fitter issues:
- Sometimes the design wouldn't fit at all, from memory, but there were easy optimisations for that I think.
- Sometimes one or more related signals wouldn't fit into the same FB, and this lead to some crazy sync issues on the screen.
- Often, changing a single pixel in the character "ROM" would make or break the fitting, and this is probably because of how it was able to optimise for similar bit patterns.
- I also had some sucess by:
- Changing the Synthesis "Optimization Effort" from "Normal" to "High", while leaving it on "Speed".
- Changing the Fitter to "Optimize Density", optionally with "Exhaustive" mode (though I don't think it was ever actually used).
- To help example this in future I decided to start adding
.rpt
files to the repo, so we can track how resources get used differently with each change. - I also had an issue at one point where it was counting erratically. I was trying to do this on the edge of
vblank
but it was not working as I expected. I think I solved this by putting all of the counter logic into anif..else
chain within the onealways @(posedge vblank)
block. I assume the problem was that sometimes, due to propagation delays, the current state of the frame counter was not consistent when being sampled by the BCD counter logic.
Other possible improvements:
- Digits are currently made up of 2×2-size pixels, i.e. double-size. Why not try quad-size, too?
- Why not more digits, to see how far we can go?
- Can we fit hex digit images into the ROM? Maybe split it across two FBs somehow? Or odd/even lines?
- Can the ROM also be improved by using on 3 pixels wide (and generating the separating 4th pixel using some other logic)? Also, could we just use one big combo
case
and ignore the unused rows? - Could we have two BCD counters, one for frames, and another for ÷25,000 (to yield a counter in actual seconds and milliseconds)?