New user help: communication between two blocks in same function


#1

Hi,

I am new to myhdl, trying to learn the language by implementing the “hello world” of VGA (the “bouncing ball”).

I started out with the vga-signal generator module, which is a pretty standard design:

  • a “clocked” block to generate internal two counters (hcounter from 0 to 799, vcounter from 0 to 520)
  • a “combinational” block to generate the sync signals, external counters and some additional signals, based on the internal counters generated in the clocked block

The code is here:

My idea is to use variables on the scope of the function to communicate between the two blocks, but this does not seams to work. The comb block is only executed once.
The changes in the variables in the clocked block do not seams to ripple throu to the comb block.

Adding “nonlocal” in the embedded functions does not help.

What am I doing wrong here?
What is the proper way to implement this?

Cheerio! Kr. Bonne.


#2

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.


#3

Hi Josy,

First my apologies or the late reply. (a busy couple of days)
Thank you very much for the answer. I know there was something fundamentatlly wrong as my code, but I couldn’t put my finger on it.

Your remark about @always_seq and the general structuring of the code is valid, but this is just development code and my first use of myhdl. I hope that my coding-skills get better in the future :slight_smile:

Thanks for the help
Cheerio! Kr. Bonne.