Introduction

The purpose of this example is to show how a demonstrator design that is originally coded in VHDL can be done in MyHDL.

The original design is here. Basically, it draws the message "Hello World" on a screen.

We will show how MyHDL can make the design task easier. For example, it is not necessary to worry about VHDL type conversions anymore: the MyHDL type system gives hardware designers exactly the right types for synthesizable code. In particular, MyHDL has integers that "just work" instead of low level signed and unsigned types. Furthermore, we will show how MyHDL's embedded scripting capabilities can be used to automate tasks that are traditionally done outside synthesizable code, resulting in a clearer and less error prone design.

It may be interesting to compare the original VHDL code with the code generated by the MyHDL convertor. Therefore, the generated VHDL and Verilog code is listed also.

Design decisions

To demonstrate the capabilities of MyHDL, we made a number of design decisions that are different from the original:

  • We start from ascii art generated by a program instead of a table of '0's and '1's as in the original code. This makes it easier to visualize the message right in the code. We use embedded scripting to generate the desired format in-place.
  • While we were at it, we used a nicer font :-)
  • The whole image is set up as a table so that we can do a one-liner indexing operation in the hardware behavior description.
  • We describe the actual hardware behavior in a sequential instead of a combinatorial process. This is a more abstract view that leads to shorter and more robust code.
  • We introduced a row variable to make the intention clearer.

MyHDL code

import re

from myhdl import *

@block
def HelloWorld(
    pixelClock,
    Red,
    Green,
    Blue,
    hSync,
    vSync
):

    ### Image ###

    # From: figlet -f alphabet " Hello World "
    MSG = [
    "    H  H     l l        W     W         l    d    ",
    "    H  H     l l        W     W         l    d    ",
    "    HHHH eee l l ooo    W  W  W ooo rrr l  ddd    ",
    "    H  H e e l l o o     W W W  o o r   l d  d    ",
    "    H  H ee  l l ooo      W W   ooo r   l  ddd    ",
    ]

    # Convert spaces, letters to 0, 1
    def to_10(s):
        """Convert letters to 1, then spaces to 0"""
        s = re.sub(r'\w', '1', s)
        s = re.sub(r'\s', '0', s)
        return s

    MSG = [to_10(s) for s in MSG]
    assert len(MSG[1]) == 50

    # Setup image as a concatenation of rows
    BORDER =  '1' +  '0' * 48 + '1'
    NULL = '0' * 50
    IMAGE = [BORDER] + [NULL] + MSG + [NULL] * (37-len(MSG)-3) + [BORDER]
    assert len(IMAGE) == 37

    # Convert strings to ints for use in convertible code
    TABLE = tuple([int(s, 2) for s in IMAGE])

    ### Hardware behavior ###

    # Timing constants
    hMaxCount = 1056-1
    hStartSync = 840
    hEndSync = 968
    vMaxCount = 628-1
    vStartSync = 601
    vEndSync = 605

    # Signals
    hCounter = Signal(intbv(0)[11:])
    vCounter = Signal(intbv(0)[10:])
    shiftReg = Signal(modbv(0)[50:])

    @always_comb
    def assign():
        v = intbv(0)[4:]
        v[3] = shiftReg[49]
        Red.next = v
        Green.next = v
        Blue.next = v

    @always(pixelClock.posedge)
    def draw():
        row = intbv(0)[6:]
        if hCounter == hMaxCount:
            hCounter.next = 0
            if vCounter == vMaxCount:
                vCounter.next = 0
            else:
                row[:] = vCounter[10:4]
                shiftReg.next = TABLE[row]
                vCounter.next = vCounter + 1
        else:
            hCounter.next = hCounter + 1
            if hCounter[4:] == 15:
                shiftReg.next = shiftReg << 1

        hSync.next = hCounter >= hStartSync and hCounter < hEndSync
        vSync.next = vCounter >= vStartSync and vCounter < vEndSync

    return assign, draw

Issues

  • This is a draft version - work in progress
  • No simulations done yet, so there will be bugs :-)
  • Where is the reset???

Conversion to VHDL and Verilog

The MyHDL code can be converted to VHDL and Verilog as follows:

### Conversion to VHDL & Verilog

pixelClock = Signal(bool(0))
Red = Signal(intbv(0)[4:])
Green = Signal(intbv(0)[4:])
Blue = Signal(intbv(0)[4:])
hSync = Signal(bool(0))
vsync = Signal(bool(0))

convInst = HelloWorld(
      pixelClock,
      Red,
      Green,
      Blue,
      hSync,
      vsync)
for f in ('VHDL', 'Verilog'):
    convInst.convert(hdl=f)
    convInst.convert(hdl=f)

Generated VHDL code

-- File: HelloWorld.vhd
-- Generated by MyHDL 0.11
-- Date: Mon May  9 09:30:56 2022


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use std.textio.all;

use work.pck_myhdl_011.all;

entity HelloWorld is
    port (
        pixelClock: in std_logic;
        Red: out unsigned(3 downto 0);
        Green: out unsigned(3 downto 0);
        Blue: out unsigned(3 downto 0);
        hSync: out std_logic;
        vSync: out std_logic
    );
end entity HelloWorld;


architecture MyHDL of HelloWorld is

signal hCounter: unsigned(10 downto 0);
signal shiftReg: unsigned(49 downto 0);
signal vCounter: unsigned(9 downto 0);

begin


HELLOWORLD_ASSIGN: process (shiftReg) is
    variable v: unsigned(3 downto 0);
begin
    v := to_unsigned(0, 4);
    v(3) := shiftReg(49);
    Red <= v;
    Green <= v;
    Blue <= v;
end process HELLOWORLD_ASSIGN;


HELLOWORLD_DRAW: process (pixelClock) is
    variable row: unsigned(5 downto 0);
begin
    if rising_edge(pixelClock) then
        row := to_unsigned(0, 6);
        if (hCounter = 1055) then
            hCounter <= to_unsigned(0, 11);
            if (vCounter = 627) then
                vCounter <= to_unsigned(0, 10);
            else
                row := vCounter(10-1 downto 4);
                case to_integer(row) is
                    when 0 => shiftReg <= "10000000000000000000000000000000000000000000000001";
                    when 1 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 2 => shiftReg <= "00001001000001010000000010000010000000001000010000";
                    when 3 => shiftReg <= "00001001000001010000000010000010000000001000010000";
                    when 4 => shiftReg <= "00001111011101010111000010010010111011101001110000";
                    when 5 => shiftReg <= "00001001010101010101000001010100101010001010010000";
                    when 6 => shiftReg <= "00001001011001010111000000101000111010001001110000";
                    when 7 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 8 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 9 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 10 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 11 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 12 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 13 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 14 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 15 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 16 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 17 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 18 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 19 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 20 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 21 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 22 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 23 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 24 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 25 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 26 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 27 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 28 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 29 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 30 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 31 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 32 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 33 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 34 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when 35 => shiftReg <= "00000000000000000000000000000000000000000000000000";
                    when others => shiftReg <= "10000000000000000000000000000000000000000000000001";
                end case;
                vCounter <= (vCounter + 1);
            end if;
        else
            hCounter <= (hCounter + 1);
            if (hCounter(4-1 downto 0) = 15) then
                shiftReg <= shift_left(shiftReg, 1);
            end if;
        end if;
        hSync <= stdl((hCounter >= 840) and (hCounter < 968));
        vSync <= stdl((vCounter >= 601) and (vCounter < 605));
    end if;
end process HELLOWORLD_DRAW;


end architecture MyHDL;

Generated Verilog code

// File: HelloWorld.v
// Generated by MyHDL 0.11
// Date: Mon May  9 09:30:56 2022


`timescale 1ns/10ps

module HelloWorld (
    pixelClock,
    Red,
    Green,
    Blue,
    hSync,
    vSync
);

input pixelClock;
output [3:0] Red;
reg [3:0] Red;
output [3:0] Green;
reg [3:0] Green;
output [3:0] Blue;
reg [3:0] Blue;
output hSync;
reg hSync;
output vSync;
reg vSync;

reg [10:0] hCounter;
reg [49:0] shiftReg;
reg [9:0] vCounter;



always @(shiftReg) begin: HELLOWORLD_ASSIGN
    reg [4-1:0] v;
    v = 4'h0;
    v[3] = shiftReg[49];
    Red = v;
    Green = v;
    Blue = v;
end


always @(posedge pixelClock) begin: HELLOWORLD_DRAW
    reg [6-1:0] row;
    row = 6'h0;
    if ((hCounter == 1055)) begin
        hCounter <= 0;
        if ((vCounter == 627)) begin
            vCounter <= 0;
        end
        else begin
            row = vCounter[10-1:4];
            case (row)
                0: shiftReg <= 51'h2000000000001;
                1: shiftReg <= 0;
                2: shiftReg <= 47'h241402080210;
                3: shiftReg <= 47'h241402080210;
                4: shiftReg <= 47'h3dd5c24bba70;
                5: shiftReg <= 47'h25554152a290;
                6: shiftReg <= 47'h2595c0a3a270;
                7: shiftReg <= 0;
                8: shiftReg <= 0;
                9: shiftReg <= 0;
                10: shiftReg <= 0;
                11: shiftReg <= 0;
                12: shiftReg <= 0;
                13: shiftReg <= 0;
                14: shiftReg <= 0;
                15: shiftReg <= 0;
                16: shiftReg <= 0;
                17: shiftReg <= 0;
                18: shiftReg <= 0;
                19: shiftReg <= 0;
                20: shiftReg <= 0;
                21: shiftReg <= 0;
                22: shiftReg <= 0;
                23: shiftReg <= 0;
                24: shiftReg <= 0;
                25: shiftReg <= 0;
                26: shiftReg <= 0;
                27: shiftReg <= 0;
                28: shiftReg <= 0;
                29: shiftReg <= 0;
                30: shiftReg <= 0;
                31: shiftReg <= 0;
                32: shiftReg <= 0;
                33: shiftReg <= 0;
                34: shiftReg <= 0;
                35: shiftReg <= 0;
                default: shiftReg <= 51'h2000000000001;
            endcase
            vCounter <= (vCounter + 1);
        end
    end
    else begin
        hCounter <= (hCounter + 1);
        if ((hCounter[4-1:0] == 15)) begin
            shiftReg <= (shiftReg << 1);
        end
    end
    hSync <= ((hCounter >= 840) && (hCounter < 968));
    vSync <= ((vCounter >= 601) && (vCounter < 605));
end

endmodule