2008-11-16 09:15:32 +01:00
|
|
|
.. currentmodule:: myhdl
|
|
|
|
|
|
|
|
.. _conv-usage:
|
|
|
|
|
|
|
|
*******************
|
|
|
|
Conversion examples
|
|
|
|
*******************
|
|
|
|
|
|
|
|
|
|
|
|
.. _conv-usage-intro:
|
|
|
|
|
|
|
|
Introduction
|
|
|
|
============
|
|
|
|
|
|
|
|
In this chapter, we will demonstrate the conversion process with a
|
|
|
|
number of examples. For the concepts of MyHDL conversion,
|
|
|
|
read the companion chapter :ref:`conv`.
|
|
|
|
|
|
|
|
.. _conv-usage-seq:
|
|
|
|
|
|
|
|
A small sequential design
|
|
|
|
=========================
|
|
|
|
|
|
|
|
Consider the following MyHDL code for an incrementer module::
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
|
|
|
|
def Inc(count, enable, clock, reset):
|
|
|
|
|
|
|
|
""" Incrementer with enable.
|
|
|
|
|
|
|
|
count -- output
|
|
|
|
enable -- control input, increment when 1
|
|
|
|
clock -- clock input
|
|
|
|
reset -- asynchronous reset input
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
@always_seq(clock.posedge, reset=reset)
|
|
|
|
def incLogic():
|
|
|
|
if enable:
|
|
|
|
count.next = count + 1
|
|
|
|
|
|
|
|
return incLogic
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
2008-11-23 23:24:38 +01:00
|
|
|
Normally, to simulate the design, we would elaborate it as follows::
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
m = 8
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
count = Signal(modbv(0)[m:])
|
|
|
|
enable = Signal(bool(0))
|
|
|
|
clock = Signal(bool(0))
|
|
|
|
reset = ResetSignal(0, active=0, async=True)
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
inc_inst = Inc(count, enable, clock, reset)
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
``inc_inst`` is an elaborated design instance that can be simulated. To convert
|
|
|
|
it to Verilog, we change the last line as follows::
|
|
|
|
|
2015-03-25 07:57:24 -05:00
|
|
|
inc_inst = toVerilog(Inc, count, enable, clock, reset)
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
Again, this creates an instance that can be simulated, but as a side effect, it
|
2008-11-23 22:36:16 +01:00
|
|
|
also generates an equivalent Verilog module in file :file:`Inc.v`. The Verilog
|
2008-11-16 09:15:32 +01:00
|
|
|
code looks as follows::
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
module Inc (
|
2012-12-21 17:08:50 +01:00
|
|
|
count,
|
|
|
|
enable,
|
|
|
|
clock,
|
|
|
|
reset
|
2008-11-23 22:36:16 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
output [7:0] count;
|
|
|
|
reg [7:0] count;
|
|
|
|
input enable;
|
|
|
|
input clock;
|
|
|
|
input reset;
|
|
|
|
|
|
|
|
always @(posedge clock, negedge reset) begin: INC_INCLOGIC
|
2012-12-21 17:08:50 +01:00
|
|
|
if (reset == 0) begin
|
|
|
|
count <= 0;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
if (enable) begin
|
|
|
|
count <= (count + 1);
|
|
|
|
end
|
|
|
|
end
|
2008-11-23 22:36:16 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
|
2008-11-23 23:24:38 +01:00
|
|
|
The convertor infers a proper Verilog module interface and maps
|
|
|
|
the MyHDL generator to a Verilog always block.
|
2008-11-23 22:36:16 +01:00
|
|
|
|
|
|
|
Similarly, we can convert to VHDL as follows::
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
inc_inst = toVHDL(Inc, count, enable, clock, reset, n=n)
|
2008-11-23 22:36:16 +01:00
|
|
|
|
|
|
|
This creates an equivalent VHDL module in file :file:`Inc.vhd`::
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
library IEEE;
|
|
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
use IEEE.numeric_std.all;
|
|
|
|
use std.textio.all;
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
use work.pck_myhdl_08.all;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
entity Inc is
|
|
|
|
port (
|
|
|
|
count: inout unsigned(7 downto 0);
|
|
|
|
enable: in std_logic;
|
|
|
|
clock: in std_logic;
|
|
|
|
reset: in std_logic
|
|
|
|
);
|
|
|
|
end entity Inc;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
architecture MyHDL of Inc is
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
begin
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
INC_INCLOGIC: process (clock, reset) is
|
|
|
|
begin
|
|
|
|
if (reset = '0') then
|
|
|
|
count <= (others => '0');
|
|
|
|
elsif rising_edge(clock) then
|
|
|
|
if bool(enable) then
|
|
|
|
count <= (count + 1);
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
end process INC_INCLOGIC;
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
end architecture MyHDL;
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 23:24:38 +01:00
|
|
|
The MyHDL generator is mapped to a VHDL process in this case.
|
|
|
|
|
|
|
|
Note that the VHDL file refers to a VHDL package called
|
2012-12-21 17:08:50 +01:00
|
|
|
``pck_myhdl_08``. This package contains a number of convenience
|
2008-11-23 23:24:38 +01:00
|
|
|
functions that make the conversion easier.
|
|
|
|
|
|
|
|
Note also the use of an ``inout`` in the interface. This is not
|
|
|
|
recommended VHDL design practice, but it is required here to have a
|
|
|
|
valid VHDL design that matches the behavior of the MyHDL design. As
|
|
|
|
this is only an issue for ports and as the convertor output is
|
|
|
|
non-hierarchical, the issue is not very common and has an easy
|
|
|
|
workaround.
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
.. _conv-usage-comb:
|
|
|
|
|
|
|
|
A small combinatorial design
|
|
|
|
============================
|
|
|
|
|
|
|
|
The second example is a small combinatorial design, more specifically the binary
|
|
|
|
to Gray code converter from previous chapters::
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
def bin2gray(B, G, width):
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
""" Gray encoder.
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
B -- input intbv signal, binary encoded
|
|
|
|
G -- output intbv signal, gray encoded
|
|
|
|
width -- bit width
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
"""
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
@always_comb
|
|
|
|
def logic():
|
|
|
|
Bext = intbv(0)[width+1:]
|
|
|
|
Bext[:] = B
|
|
|
|
for i in range(width):
|
|
|
|
G.next[i] = Bext[i+1] ^ Bext[i]
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
return logic
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
As before, you can create an instance and convert to Verilog and VHDL as follows::
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
width = 8
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
B = Signal(intbv(0)[width:])
|
|
|
|
G = Signal(intbv(0)[width:])
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
bin2gray_inst = toVerilog(bin2gray, B, G, width)
|
|
|
|
bin2gray_inst = toVHDL(bin2gray, B, G, width)
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
The generated Verilog code looks as follows::
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
module bin2gray (
|
|
|
|
B,
|
|
|
|
G
|
|
|
|
);
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
input [7:0] B;
|
|
|
|
output [7:0] G;
|
|
|
|
reg [7:0] G;
|
2008-11-23 22:36:16 +01:00
|
|
|
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
always @(B) begin: BIN2GRAY_LOGIC
|
|
|
|
integer i;
|
|
|
|
reg [9-1:0] Bext;
|
|
|
|
Bext = 9'h0;
|
|
|
|
Bext = B;
|
|
|
|
for (i=0; i<8; i=i+1) begin
|
|
|
|
G[i] <= (Bext[(i + 1)] ^ Bext[i]);
|
|
|
|
end
|
|
|
|
end
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
endmodule
|
2008-11-23 22:36:16 +01:00
|
|
|
|
|
|
|
|
|
|
|
The generated VHDL code looks as follows::
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
library IEEE;
|
|
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
use IEEE.numeric_std.all;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
use work.pck_myhdl_08.all;
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
entity bin2gray is
|
|
|
|
port (
|
|
|
|
B: in unsigned(7 downto 0);
|
|
|
|
G: out unsigned(7 downto 0)
|
|
|
|
);
|
|
|
|
end entity bin2gray;
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
architecture MyHDL of bin2gray is
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
begin
|
2008-11-23 22:36:16 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
BIN2GRAY_LOGIC: process (B) is
|
|
|
|
variable Bext: unsigned(8 downto 0);
|
|
|
|
begin
|
|
|
|
Bext := to_unsigned(0, 9);
|
|
|
|
Bext := resize(B, 9);
|
|
|
|
for i in 0 to 8-1 loop
|
|
|
|
G(i) <= (Bext((i + 1)) xor Bext(i));
|
|
|
|
end loop;
|
|
|
|
end process BIN2GRAY_LOGIC;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
end architecture MyHDL;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
.. _conv-usage-hier:
|
|
|
|
|
|
|
|
A hierarchical design
|
|
|
|
=====================
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
The converter can handle designs with an arbitrarily deep hierarchy.
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
For example, suppose we want to design an incrementer with Gray code output.
|
|
|
|
Using the designs from previous sections, we can proceed as follows::
|
|
|
|
|
|
|
|
def GrayInc(graycnt, enable, clock, reset, width):
|
|
|
|
|
|
|
|
bincnt = Signal(intbv(0)[width:])
|
|
|
|
|
|
|
|
inc_1 = inc(bincnt, enable, clock, reset, n=2**width)
|
|
|
|
bin2gray_1 = bin2gray(B=bincnt, G=graycnt, width=width)
|
|
|
|
|
|
|
|
return inc_1, bin2gray_1
|
|
|
|
|
|
|
|
According to Gray code properties, only a single bit will change in consecutive
|
|
|
|
values. However, as the ``bin2gray`` module is combinatorial, the output bits
|
|
|
|
may have transient glitches, which may not be desirable. To solve this, let's
|
|
|
|
create an additional level of hierarchy and add an output register to the
|
|
|
|
design. (This will create an additional latency of a clock cycle, which may not
|
|
|
|
be acceptable, but we will ignore that here.) ::
|
|
|
|
|
|
|
|
def GrayIncReg(graycnt, enable, clock, reset, width):
|
|
|
|
|
|
|
|
graycnt_comb = Signal(intbv(0)[width:])
|
|
|
|
gray_inc_1 = GrayInc(graycnt_comb, enable, clock, reset, width)
|
|
|
|
|
|
|
|
@always(clock.posedge)
|
|
|
|
def reg_1():
|
|
|
|
graycnt.next = graycnt_comb
|
|
|
|
|
|
|
|
return gray_inc_1, reg_1
|
|
|
|
|
|
|
|
We can convert this hierarchical design as before::
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
width = 8
|
|
|
|
graycnt = Signal(modbv(0)[width:])
|
|
|
|
enable = Signal(bool())
|
|
|
|
clock = Signal(bool())
|
|
|
|
reset = ResetSignal(0, active=0, async=True)
|
|
|
|
|
|
|
|
toVerilog(GrayIncReg, graycnt, enable, clock, reset, width)
|
|
|
|
toVHDL(GrayIncReg, graycnt, enable, clock, reset, width)
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
The Verilog output code looks as follows::
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
module GrayIncReg (
|
2012-12-21 17:08:50 +01:00
|
|
|
graycnt,
|
|
|
|
enable,
|
|
|
|
clock,
|
|
|
|
reset
|
2008-11-23 22:36:16 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
output [7:0] graycnt;
|
|
|
|
reg [7:0] graycnt;
|
|
|
|
input enable;
|
|
|
|
input clock;
|
|
|
|
input reset;
|
|
|
|
|
|
|
|
reg [7:0] graycnt_comb;
|
|
|
|
reg [7:0] gray_inc_1_bincnt;
|
|
|
|
|
|
|
|
always @(posedge clock, negedge reset) begin: GRAYINCREG_GRAY_INC_1_INC_1_INCLOGIC
|
2012-12-21 17:08:50 +01:00
|
|
|
if (reset == 0) begin
|
|
|
|
gray_inc_1_bincnt <= 0;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
if (enable) begin
|
|
|
|
gray_inc_1_bincnt <= (gray_inc_1_bincnt + 1);
|
|
|
|
end
|
|
|
|
end
|
2008-11-23 22:36:16 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
always @(gray_inc_1_bincnt) begin: GRAYINCREG_GRAY_INC_1_BIN2GRAY_1_LOGIC
|
2012-12-21 17:08:50 +01:00
|
|
|
integer i;
|
|
|
|
reg [9-1:0] Bext;
|
|
|
|
Bext = 9'h0;
|
|
|
|
Bext = gray_inc_1_bincnt;
|
|
|
|
for (i=0; i<8; i=i+1) begin
|
|
|
|
graycnt_comb[i] = (Bext[(i + 1)] ^ Bext[i]);
|
|
|
|
end
|
2008-11-23 22:36:16 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
always @(posedge clock) begin: GRAYINCREG_REG_1
|
2012-12-21 17:08:50 +01:00
|
|
|
graycnt <= graycnt_comb;
|
2008-11-23 22:36:16 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
The VHDL output code looks as follows::
|
|
|
|
|
|
|
|
library IEEE;
|
|
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
use IEEE.numeric_std.all;
|
2012-12-21 17:08:50 +01:00
|
|
|
use work.pck_myhdl_08.all;
|
2008-11-23 22:36:16 +01:00
|
|
|
|
|
|
|
entity GrayIncReg is
|
2012-12-21 17:08:50 +01:00
|
|
|
port (
|
|
|
|
graycnt: out unsigned(7 downto 0);
|
|
|
|
enable: in std_logic;
|
|
|
|
clock: in std_logic;
|
|
|
|
reset: in std_logic
|
|
|
|
);
|
2008-11-23 22:36:16 +01:00
|
|
|
end entity GrayIncReg;
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
architecture MyHDL of GrayIncReg is
|
|
|
|
|
|
|
|
signal graycnt_comb: unsigned(7 downto 0);
|
|
|
|
signal gray_inc_1_bincnt: unsigned(7 downto 0);
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
GRAYINCREG_GRAY_INC_1_INC_1_INCLOGIC: process (clock, reset) is
|
|
|
|
begin
|
2012-12-21 17:08:50 +01:00
|
|
|
if (reset = '0') then
|
|
|
|
gray_inc_1_bincnt <= (others => '0');
|
|
|
|
elsif rising_edge(clock) then
|
|
|
|
if bool(enable) then
|
|
|
|
gray_inc_1_bincnt <= (gray_inc_1_bincnt + 1);
|
|
|
|
end if;
|
|
|
|
end if;
|
2008-11-23 22:36:16 +01:00
|
|
|
end process GRAYINCREG_GRAY_INC_1_INC_1_INCLOGIC;
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
GRAYINCREG_GRAY_INC_1_BIN2GRAY_1_LOGIC: process (gray_inc_1_bincnt) is
|
2012-12-21 17:08:50 +01:00
|
|
|
variable Bext: unsigned(8 downto 0);
|
2008-11-23 22:36:16 +01:00
|
|
|
begin
|
2012-12-21 17:08:50 +01:00
|
|
|
Bext := to_unsigned(0, 9);
|
|
|
|
Bext := resize(gray_inc_1_bincnt, 9);
|
|
|
|
for i in 0 to 8-1 loop
|
|
|
|
graycnt_comb(i) <= (Bext((i + 1)) xor Bext(i));
|
|
|
|
end loop;
|
2008-11-23 22:36:16 +01:00
|
|
|
end process GRAYINCREG_GRAY_INC_1_BIN2GRAY_1_LOGIC;
|
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
GRAYINCREG_REG_1: process (clock) is
|
|
|
|
begin
|
2012-12-21 17:08:50 +01:00
|
|
|
if rising_edge(clock) then
|
|
|
|
graycnt <= graycnt_comb;
|
|
|
|
end if;
|
2008-11-23 22:36:16 +01:00
|
|
|
end process GRAYINCREG_REG_1;
|
|
|
|
|
|
|
|
end architecture MyHDL;
|
|
|
|
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2012-12-21 17:08:50 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2008-11-16 09:15:32 +01:00
|
|
|
Note that the output is a flat "net list of blocks", and that hierarchical
|
|
|
|
signal names are generated as necessary.
|
|
|
|
|
|
|
|
|
|
|
|
.. _conv-usage-fsm:
|
|
|
|
|
|
|
|
Optimizations for finite state machines
|
|
|
|
=======================================
|
|
|
|
|
|
|
|
As often in hardware design, finite state machines deserve special attention.
|
|
|
|
|
|
|
|
In Verilog and VHDL, finite state machines are typically described using case
|
|
|
|
statements. Python doesn't have a case statement, but the converter recognizes
|
|
|
|
particular if-then-else structures and maps them to case statements. This
|
|
|
|
optimization occurs when a variable whose type is an enumerated type is
|
|
|
|
sequentially tested against enumeration items in an if-then-else structure.
|
|
|
|
Also, the appropriate synthesis pragmas for efficient synthesis are generated in
|
|
|
|
the Verilog code.
|
|
|
|
|
|
|
|
As a further optimization, function :func:`enum` was enhanced to support
|
|
|
|
alternative encoding schemes elegantly, using an additional parameter
|
|
|
|
*encoding*. For example::
|
|
|
|
|
|
|
|
t_State = enum('SEARCH', 'CONFIRM', 'SYNC', encoding='one_hot')
|
|
|
|
|
|
|
|
The default encoding is ``'binary'``; the other possibilities are ``'one_hot'``
|
|
|
|
and ``'one_cold'``. This parameter only affects the conversion output, not the
|
|
|
|
behavior of the type. The generated Verilog code for case statements is
|
|
|
|
optimized for an efficient implementation according to the encoding. Note that
|
|
|
|
in contrast, a Verilog designer has to make nontrivial code changes to implement
|
|
|
|
a different encoding scheme.
|
|
|
|
|
|
|
|
As an example, consider the following finite state machine, whose state variable
|
|
|
|
uses the enumeration type defined above::
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
ACTIVE_LOW = bool(0)
|
|
|
|
FRAME_SIZE = 8
|
|
|
|
t_State = enum('SEARCH', 'CONFIRM', 'SYNC', encoding="one_hot")
|
|
|
|
|
|
|
|
def FramerCtrl(SOF, state, syncFlag, clk, reset_n):
|
|
|
|
|
|
|
|
""" Framing control FSM.
|
|
|
|
|
|
|
|
SOF -- start-of-frame output bit
|
|
|
|
state -- FramerState output
|
|
|
|
syncFlag -- sync pattern found indication input
|
|
|
|
clk -- clock input
|
|
|
|
reset_n -- active low reset
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
index = Signal(intbv(0)[8:]) # position in frame
|
|
|
|
|
|
|
|
@always(clk.posedge, reset_n.negedge)
|
|
|
|
def FSM():
|
|
|
|
if reset_n == ACTIVE_LOW:
|
|
|
|
SOF.next = 0
|
|
|
|
index.next = 0
|
|
|
|
state.next = t_State.SEARCH
|
|
|
|
else:
|
|
|
|
index.next = (index + 1) % FRAME_SIZE
|
|
|
|
SOF.next = 0
|
|
|
|
if state == t_State.SEARCH:
|
|
|
|
index.next = 1
|
|
|
|
if syncFlag:
|
|
|
|
state.next = t_State.CONFIRM
|
|
|
|
elif state == t_State.CONFIRM:
|
|
|
|
if index == 0:
|
|
|
|
if syncFlag:
|
|
|
|
state.next = t_State.SYNC
|
|
|
|
else:
|
|
|
|
state.next = t_State.SEARCH
|
|
|
|
elif state == t_State.SYNC:
|
|
|
|
if index == 0:
|
|
|
|
if not syncFlag:
|
|
|
|
state.next = t_State.SEARCH
|
|
|
|
SOF.next = (index == FRAME_SIZE-1)
|
|
|
|
else:
|
|
|
|
raise ValueError("Undefined state")
|
|
|
|
|
|
|
|
return FSM
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
The conversion is done as before::
|
|
|
|
|
|
|
|
SOF = Signal(bool(0))
|
|
|
|
syncFlag = Signal(bool(0))
|
|
|
|
clk = Signal(bool(0))
|
|
|
|
reset_n = Signal(bool(1))
|
|
|
|
state = Signal(t_State.SEARCH)
|
2008-11-23 22:36:16 +01:00
|
|
|
toVerilog(FramerCtrl, SOF, state, syncFlag, clk, reset_n)
|
|
|
|
toVHDL(FramerCtrl, SOF, state, syncFlag, clk, reset_n)
|
|
|
|
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
The Verilog output looks as follows::
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
module FramerCtrl (
|
|
|
|
SOF,
|
|
|
|
state,
|
|
|
|
syncFlag,
|
|
|
|
clk,
|
|
|
|
reset_n
|
|
|
|
);
|
|
|
|
|
|
|
|
output SOF;
|
|
|
|
reg SOF;
|
|
|
|
output [2:0] state;
|
|
|
|
reg [2:0] state;
|
|
|
|
input syncFlag;
|
|
|
|
input clk;
|
|
|
|
input reset_n;
|
|
|
|
|
|
|
|
reg [7:0] index;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
always @(posedge clk, negedge reset_n) begin: FRAMERCTRL_FSM
|
|
|
|
if ((reset_n == 0)) begin
|
|
|
|
SOF <= 0;
|
|
|
|
index <= 0;
|
|
|
|
state <= 3'b001;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
index <= ((index + 1) % 8);
|
|
|
|
SOF <= 0;
|
|
|
|
// synthesis parallel_case full_case
|
|
|
|
casez (state)
|
|
|
|
3'b??1: begin
|
|
|
|
index <= 1;
|
|
|
|
if (syncFlag) begin
|
|
|
|
state <= 3'b010;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
3'b?1?: begin
|
|
|
|
if ((index == 0)) begin
|
|
|
|
if (syncFlag) begin
|
|
|
|
state <= 3'b100;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
state <= 3'b001;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
3'b1??: begin
|
|
|
|
if ((index == 0)) begin
|
|
|
|
if ((!syncFlag)) begin
|
|
|
|
state <= 3'b001;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
SOF <= (index == (8 - 1));
|
|
|
|
end
|
|
|
|
default: begin
|
|
|
|
$finish;
|
|
|
|
end
|
|
|
|
endcase
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
The VHDL output looks as follows::
|
|
|
|
|
|
|
|
package pck_FramerCtrl is
|
|
|
|
|
|
|
|
type t_enum_t_State_1 is (
|
|
|
|
SEARCH,
|
|
|
|
CONFIRM,
|
|
|
|
SYNC
|
|
|
|
);
|
|
|
|
attribute enum_encoding of t_enum_t_State_1: type is "001 010 100";
|
|
|
|
|
|
|
|
end package pck_FramerCtrl;
|
|
|
|
|
|
|
|
library IEEE;
|
|
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
use IEEE.numeric_std.all;
|
|
|
|
use std.textio.all;
|
|
|
|
|
|
|
|
use work.pck_myhdl_06.all;
|
|
|
|
|
|
|
|
use work.pck_FramerCtrl.all;
|
|
|
|
|
|
|
|
entity FramerCtrl is
|
|
|
|
port (
|
|
|
|
SOF: out std_logic;
|
|
|
|
state: inout t_enum_t_State_1;
|
|
|
|
syncFlag: in std_logic;
|
|
|
|
clk: in std_logic;
|
|
|
|
reset_n: in std_logic
|
|
|
|
);
|
|
|
|
end entity FramerCtrl;
|
|
|
|
|
|
|
|
architecture MyHDL of FramerCtrl is
|
|
|
|
|
|
|
|
signal index: unsigned(7 downto 0);
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
|
|
|
|
FRAMERCTRL_FSM: process (clk, reset_n) is
|
|
|
|
begin
|
|
|
|
if (reset_n = '0') then
|
|
|
|
SOF <= '0';
|
|
|
|
index <= "00000000";
|
|
|
|
state <= SEARCH;
|
|
|
|
elsif rising_edge(clk) then
|
|
|
|
index <= ((index + 1) mod 8);
|
|
|
|
SOF <= '0';
|
|
|
|
case state is
|
|
|
|
when SEARCH =>
|
|
|
|
index <= "00000001";
|
|
|
|
if to_boolean(syncFlag) then
|
|
|
|
state <= CONFIRM;
|
|
|
|
end if;
|
|
|
|
when CONFIRM =>
|
|
|
|
if (index = 0) then
|
|
|
|
if to_boolean(syncFlag) then
|
|
|
|
state <= SYNC;
|
|
|
|
else
|
|
|
|
state <= SEARCH;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
when SYNC =>
|
|
|
|
if (index = 0) then
|
|
|
|
if (not to_boolean(syncFlag)) then
|
|
|
|
state <= SEARCH;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
SOF <= to_std_logic(signed(resize(index, 9)) = (8 - 1));
|
|
|
|
when others =>
|
|
|
|
assert False report "End of Simulation" severity Failure;
|
|
|
|
end case;
|
|
|
|
end if;
|
|
|
|
end process FRAMERCTRL_FSM;
|
|
|
|
|
|
|
|
end architecture MyHDL;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
2008-11-24 21:26:26 +01:00
|
|
|
.. _conv-usage-ram:
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
RAM inference
|
|
|
|
=============
|
|
|
|
|
2008-11-23 23:24:38 +01:00
|
|
|
Certain synthesis tools can infer RAM structures. To support
|
|
|
|
this feature, the converter maps lists of signals in MyHDL
|
|
|
|
to Verilog memories and VHDL arrays.
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
The following MyHDL example is a ram model that uses a list of signals to model
|
|
|
|
the internal memory. ::
|
|
|
|
|
|
|
|
def RAM(dout, din, addr, we, clk, depth=128):
|
|
|
|
""" Ram model """
|
|
|
|
|
|
|
|
mem = [Signal(intbv(0)[8:]) for i in range(depth)]
|
|
|
|
|
|
|
|
@always(clk.posedge)
|
|
|
|
def write():
|
|
|
|
if we:
|
2010-12-19 09:58:05 +01:00
|
|
|
mem[addr].next = din
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
@always_comb
|
|
|
|
def read():
|
2010-12-19 09:58:05 +01:00
|
|
|
dout.next = mem[addr]
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
return write, read
|
|
|
|
|
|
|
|
With the appropriate signal definitions for the interface ports, it is converted
|
|
|
|
to the following Verilog code. Note how the list of signals ``mem`` is mapped to
|
|
|
|
a Verilog memory. ::
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
module ram (
|
|
|
|
dout,
|
|
|
|
din,
|
|
|
|
addr,
|
|
|
|
we,
|
|
|
|
clk
|
|
|
|
);
|
|
|
|
|
|
|
|
output [7:0] dout;
|
|
|
|
wire [7:0] dout;
|
|
|
|
input [7:0] din;
|
|
|
|
input [6:0] addr;
|
|
|
|
input we;
|
|
|
|
input clk;
|
|
|
|
|
|
|
|
|
|
|
|
reg [7:0] mem [0:128-1];
|
|
|
|
|
|
|
|
|
|
|
|
always @(posedge clk) begin: RAM_1_WRITE
|
|
|
|
if (we) begin
|
|
|
|
mem[addr] <= din;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
assign dout = mem[addr];
|
|
|
|
|
|
|
|
endmodule
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
In VHDL, the list of MyHDL signals is modeled as a VHDL array signal::
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
library IEEE;
|
|
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
use IEEE.numeric_std.all;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
use work.pck_myhdl_06.all;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
entity ram is
|
|
|
|
port (
|
|
|
|
dout: out unsigned(7 downto 0);
|
|
|
|
din: in unsigned(7 downto 0);
|
|
|
|
addr: in unsigned(6 downto 0);
|
|
|
|
we: in std_logic;
|
|
|
|
clk: in std_logic
|
|
|
|
);
|
|
|
|
end entity ram;
|
|
|
|
|
|
|
|
architecture MyHDL of ram is
|
|
|
|
|
|
|
|
type t_array_mem is array(0 to 128-1) of unsigned(7 downto 0);
|
|
|
|
signal mem: t_array_mem;
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
RAM_WRITE: process (clk) is
|
|
|
|
begin
|
|
|
|
if rising_edge(clk) then
|
|
|
|
if to_boolean(we) then
|
|
|
|
mem(to_integer(addr)) <= din;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
end process RAM_WRITE;
|
|
|
|
|
|
|
|
|
|
|
|
dout <= mem(to_integer(addr));
|
|
|
|
|
|
|
|
end architecture MyHDL;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2008-11-24 21:26:26 +01:00
|
|
|
.. _conv-usage-rom:
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
ROM inference
|
|
|
|
=============
|
|
|
|
|
|
|
|
Some synthesis tools can infer a ROM memory from a case statement. The Verilog
|
|
|
|
converter can perform the expansion into a case statement automatically, based
|
|
|
|
on a higher level description. The ROM access is described in a single line, by
|
|
|
|
indexing into a tuple of integers. The tuple can be described manually, but also
|
|
|
|
by programmatical means. Note that a tuple is used instead of a list to stress
|
|
|
|
the read-only character of the memory.
|
|
|
|
|
|
|
|
The following example illustrates this functionality. ROM access is described as
|
|
|
|
follows::
|
|
|
|
|
|
|
|
def rom(dout, addr, CONTENT):
|
|
|
|
|
|
|
|
@always_comb
|
|
|
|
def read():
|
|
|
|
dout.next = CONTENT[int(addr)]
|
|
|
|
|
|
|
|
return read
|
|
|
|
|
|
|
|
The ROM content is described as a tuple of integers. When the ROM content is
|
|
|
|
defined, the conversion can be performed::
|
|
|
|
|
|
|
|
CONTENT = (17, 134, 52, 9)
|
|
|
|
dout = Signal(intbv(0)[8:])
|
|
|
|
addr = Signal(intbv(0)[4:])
|
|
|
|
|
|
|
|
toVerilog(rom, dout, addr, CONTENT)
|
2008-11-23 22:36:16 +01:00
|
|
|
toVHDL(rom, dout, addr, CONTENT)
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
The Verilog output code is as follows::
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
module rom (
|
|
|
|
dout,
|
|
|
|
addr
|
|
|
|
);
|
|
|
|
|
|
|
|
output [7:0] dout;
|
|
|
|
reg [7:0] dout;
|
|
|
|
input [3:0] addr;
|
|
|
|
|
|
|
|
always @(addr) begin: ROM_READ
|
|
|
|
// synthesis parallel_case full_case
|
|
|
|
case (addr)
|
|
|
|
0: dout <= 17;
|
|
|
|
1: dout <= 134;
|
|
|
|
2: dout <= 52;
|
|
|
|
default: dout <= 9;
|
|
|
|
endcase
|
|
|
|
end
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
|
|
|
|
The VHDL output code is as follows::
|
|
|
|
|
|
|
|
|
|
|
|
library IEEE;
|
|
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
use IEEE.numeric_std.all;
|
|
|
|
use std.textio.all;
|
|
|
|
|
|
|
|
use work.pck_myhdl_06.all;
|
|
|
|
|
|
|
|
entity rom is
|
|
|
|
port (
|
|
|
|
dout: out unsigned(7 downto 0);
|
|
|
|
addr: in unsigned(3 downto 0)
|
|
|
|
);
|
|
|
|
end entity rom;
|
|
|
|
|
|
|
|
architecture MyHDL of rom is
|
|
|
|
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
begin
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
ROM_READ: process (addr) is
|
|
|
|
begin
|
|
|
|
case to_integer(addr) is
|
|
|
|
when 0 => dout <= "00010001";
|
|
|
|
when 1 => dout <= "10000110";
|
|
|
|
when 2 => dout <= "00110100";
|
|
|
|
when others => dout <= "00001001";
|
|
|
|
end case;
|
|
|
|
end process ROM_READ;
|
|
|
|
|
|
|
|
end architecture MyHDL;
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
|
2011-05-04 20:51:59 +02:00
|
|
|
.. index:: single: user-defined code; example
|
|
|
|
|
2008-11-24 21:26:26 +01:00
|
|
|
.. _conv-usage-custom:
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
User-defined code
|
|
|
|
=================
|
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
MyHDL provides a way to include user-defined code during the
|
2010-12-17 11:40:21 +01:00
|
|
|
conversion process, using the special function attributes
|
|
|
|
:attr:`vhdl_code` and :attr:`verilog_code`.
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
For example::
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2010-12-17 11:40:21 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
def inc_comb(nextCount, count, n):
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
@always(count)
|
|
|
|
def logic():
|
|
|
|
# do nothing here
|
|
|
|
pass
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-23 22:36:16 +01:00
|
|
|
nextCount.driven = "wire"
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2010-12-17 11:40:21 +01:00
|
|
|
return logic
|
|
|
|
|
|
|
|
inc_comb.verilog_code =\
|
2008-11-23 22:36:16 +01:00
|
|
|
"""
|
2010-12-17 11:40:21 +01:00
|
|
|
assign $nextCount = ($count + 1) % $n;
|
2008-11-23 22:36:16 +01:00
|
|
|
"""
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2010-12-17 11:40:21 +01:00
|
|
|
inc_comb.vhdl_code =\
|
2008-11-23 22:36:16 +01:00
|
|
|
"""
|
2010-12-17 11:40:21 +01:00
|
|
|
$nextCount <= ($count + 1) mod $n;
|
2008-11-23 22:36:16 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
The converted code looks as follows in Verilog::
|
|
|
|
|
|
|
|
module inc_comb (
|
|
|
|
nextCount,
|
|
|
|
count
|
|
|
|
);
|
|
|
|
|
|
|
|
output [7:0] nextCount;
|
|
|
|
wire [7:0] nextCount;
|
|
|
|
input [7:0] count;
|
|
|
|
|
|
|
|
assign nextCount = (count + 1) % 256;
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
and as follows in VHDL::
|
|
|
|
|
|
|
|
library IEEE;
|
|
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
use IEEE.numeric_std.all;
|
|
|
|
|
|
|
|
use work.pck_myhdl_06.all;
|
|
|
|
|
|
|
|
entity inc_comb is
|
|
|
|
port (
|
|
|
|
nextCount: out unsigned(7 downto 0);
|
|
|
|
count: in unsigned(7 downto 0)
|
|
|
|
);
|
|
|
|
end entity inc_comb;
|
|
|
|
|
|
|
|
architecture MyHDL of inc_comb is
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
nextCount <= (count + 1) mod 256;
|
|
|
|
|
|
|
|
end architecture MyHDL;
|
|
|
|
|
|
|
|
|
|
|
|
In this example, conversion of the :func:`inc_comb` function is
|
2010-12-17 11:40:21 +01:00
|
|
|
bypassed and the user-defined code is inserted instead. The
|
|
|
|
user-defined code is a Python template string that
|
|
|
|
can refer to signals and parameters in the MyHDL
|
|
|
|
context through ``$``-based substitutions.
|
|
|
|
During conversion, the appropriate
|
2010-12-20 10:48:44 +01:00
|
|
|
hierarchical names and parameter values will be substituted.
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-24 21:26:26 +01:00
|
|
|
The MyHDL code contains the following assignment::
|
2008-11-16 09:15:32 +01:00
|
|
|
|
|
|
|
nextCount.driven = "wire"
|
|
|
|
|
|
|
|
This specifies that the nextCount signal is driven as a Verilog wire from this
|
2008-11-24 21:26:26 +01:00
|
|
|
module.
|
2008-11-16 09:15:32 +01:00
|
|
|
|
2008-11-24 21:26:26 +01:00
|
|
|
For more info about user-defined code, see :ref:`conv-custom`.
|