December 18, 2024

Vivado and Tcl - example 4

We are working our way through the following excellent blog posts. This is the last of the 4 (and I am honestly sorry and would like to see more).

Lesson 4

This basically repeats Lesson 1 where a counter and slice block were used to blink an LED. Now we are going to replace those two with a single RTL block and write some Verilog.

The original article does this first using the GUI, then does the same design in Tcl. I am going to skim over the GUI discussion and then see if I can work with just the Tcl and be content.

A new aspect of things is the introduction of a verilog file. This requires one line to be added to the mk_proj.tcl file:

#add_files -norecurse ./../src/rtl/blink.v
add_files -norecurse blink.v
We pull constraints.xdc and blinky-rtl.tcl into the top level also.
Then:
make project
make build
I am tempted to add an entry to the Makefile to copy the final bitstream file into the top level. And I do, like the following. This does require modifying the Makefile for each project to include the project name. Note also the "clean" entry.
PROJ=blink-led-rtl
BPATH=$(PROJ)/$(PROJ).runs/impl_1

bit:
    ls -l $(BPATH)/*.bit
    cp $(BPATH)/*.bit .

clean:
    rm -f *.jou
    rm -f *.log
    rm -rf $(PROJ)

Trouble

Something is wrong, this bitstream does not work. It should blink without any help from the PS. It does turn off the first LED (D7 at F16).

The other three LED are on with reduced brightness. I modify the Verilog to turn them all off, and this works. It is like we are not getting a clock and counting.

The fixed version

After some simulation using iVerilog and a bit of experimenting, I got it all sorted out and working, as follows:
`timescale 1ns / 1ps
`default_nettype none

module blink #
(
    // Width of data bus in bits
    parameter COUNTER_WIDTH = 27
)
(
    input  wire		clk,
    input  wire     rstn,

    output wire [3:0]         leds
);

    // reg [COUNTER_WIDTH-1:0] counter = 0;
    reg [COUNTER_WIDTH-1:0] counter;

	// This does turn 'em all off
	// assign leds = 4'b1111;
	// This does turn on D7 (the one on the left)
	// assign leds[0] = 0;
	// And this turns on d6 (the one on the right)
	// assign leds[3] = 0;

	// but this never turns on
	// as if we ain't getting no clock
    // assign leds[3] = counter[COUNTER_WIDTH-1];
    // assign leds[3] = counter[20];
    // assign leds = {counter[25],3'b111};
	assign leds = { 4{counter[25]} };

    always @(posedge clk)
        if (!rstn)
          counter <= 0;
        else begin
          counter <= counter + 1;
		end

endmodule
Note that for simulation I needed to make "leds" a reg, but then changed it back to wire (as that is required to have it in a vivado RTL block apparently). This required the assign statement, since you can only assign to a reg within an always block. With my 50 Mhz clock, this gets all four LED blinking at about 1 Hz.

Analysis

Once again, this is much faster and easier than dealing with the GUI. Combine that with doing some test and simulation using iVerilog and I can be much more productive.

I do have much more to learn about using Tcl for things I have not yet attempted. I have even read about people setting up a block model, then doing the HDL wapper, then taking that verilog and combining it with other verilog that isn't in an RTL block, i.e. verilog that the block model has no knowledge about whatsoever. Lots of exciting things are possible.


Feedback? Questions? Drop me a line!

Tom's Computer Info / [email protected]