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