There are few improvements / corrections:
You can declare the counters as:
hcounter_internal = Signal(intbv(0,min=0, max=800))
vcounter_internal = Signal(intbv(0,min=0, max=521))
so without the [10:], using min and max will calculate the width for you.
I (most of us) always declare a clocked process with @always_seq(clk.posedge, reset)
rather than with @always(clk.posedge, reset.posedge)
, but the latter works as well. The always_seq
has the advantage that the reset action is implied.
But where the simulation stumbles upon is that you forgot the .next
attributes in your clocked process.
I downloaded you code and Simulation didn’t dun at all.
You can find my changes here:
from __future__ import print_function
from myhdl import Signal, intbv, always_comb, always_seq, ResetSignal, always, instance, delay, traceSignals, Simulation
def vga640x480(clk25MhzPulse, reset, vga_hs, vga_vs, vga_videoon, vga_offscreen, hcounter_external, vcounter_external):
""" vga640x480 generator
input: 25Mhz clock
reset
output: vga_hs (horizontal sync)
vga_vs (vertical sync)
vga_videoon (video on, visible screen)
vga_offscreen (vga is above or below visible screen)
hcounter: horizontal counter: 0 to 799
vcounter: vertical counter: 0 to 520
"""
# local data
# total resolution = 800 * 521 (640x480 visible) @ 25 Mhz = 60 Hz
hcounter_internal = Signal(intbv(0, min=0, max=800))
vcounter_internal = Signal(intbv(0, min=0, max=521))
# combinatorial logic
@always_comb
def assign():
if hcounter_internal < 96:
vga_hs.next = 1
# # DEBUG
# print("1")
else:
vga_hs.next = 0
# # DEBUG
# print("0")
if vcounter_internal < 2:
vga_vs.next = 1
else:
vga_vs.next = 0
if (hcounter_internal >= 144) and (hcounter_internal < 784) and (vcounter_internal >= 31) and (vcounter_internal < 510):
vga_videoon.next = 1
else:
vga_videoon.next = 0
# copy counters to output
if hcounter_internal >= 144:
hcounter_external.next = hcounter_internal - 144
else:
hcounter_external.next = 0
if vcounter_internal >= 144:
vcounter_external.next = vcounter_internal - 144
else:
vcounter_external.next = 0
# clock driven logic
@always_seq(clk25MhzPulse.posedge, reset)
def Clock25Mhz():
# DEBUG
# print("hcounter = "+str(hcounter_internal))
# print("vcounter = "+str(vcounter_internal))
if hcounter_internal != 799:
hcounter_internal.next = hcounter_internal + 1
else:
# end of horizontal line
hcounter_internal.next = 0
if vcounter_internal != 520:
vcounter_internal.next = vcounter_internal + 1
else:
vcounter_internal.next = 0
# end horizontal and vertical counters
# end else (not reset)
return Clock25Mhz, assign
"""
test suite
"""
def test_vga640x480():
# local vars
clk25MhzPulse, vga_hs, vga_vs, vga_videoon, vga_offscreen = [Signal(bool(0)) for _ in range(5)]
reset = ResetSignal(0, 1, True)
hcounter_external = Signal(intbv(0, min=0, max=800)[10:])
vcounter_external = Signal(intbv(0, min=0, max=521)[10:])
vga640x480_inst = vga640x480(clk25MhzPulse, reset, vga_hs, vga_vs, vga_videoon, vga_offscreen, hcounter_external, vcounter_external)
@always(delay(20))
def clkgen():
clk25MhzPulse.next = not clk25MhzPulse
@instance
def stimulus():
reset.next = 1
yield delay(100)
reset.next = 0
return vga640x480_inst, clkgen, stimulus
"""
simulation
"""
def simulate(timesteps):
tb = traceSignals(test_vga640x480)
sim = Simulation(tb)
sim.run(timesteps)
simulate(800 * 521 * 40 + 5000)
You see that there is no extra work necessary to use the two communicating SIgnals, hcounter_internal and vcounter_internal, in both processes. (as they been declared on the same level as those two processes and are thus inherently visible by those two processes)
There is more to say about the way you structured your code, but it basically does what you intended.
Regards,
Josy.