MyHDL中文手册(十一)——转换示例

45 篇文章 16 订阅
35 篇文章 9 订阅

小的时序电路设计

考虑下面的增量程序块的MyHDL代码

from myhdl import block, always_seq

@block
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 seq():
        if enable:
            count.next = count + 1

    return seq

该设计可以转换成Verilog和VHDL语言。第一步是精细化elaborate,就像我们为仿真所做的那样。然后,我们可以在详细的实例上使用转换方法。

from myhdl import Signal, ResetSignal, modbv

from inc import inc

def convert_inc(hdl):
    """Convert inc block to Verilog or VHDL."""

    m = 8

    count = Signal(modbv(0)[m:])
    enable = Signal(bool(0))
    clock  = Signal(bool(0))
    reset = ResetSignal(0, active=0, async=True)

    inc_1 = inc(count, enable, clock, reset)

    inc_1.convert(hdl=hdl)


convert_inc(hdl='Verilog')
convert_inc(hdl='VHDL')

为了灵活起见,我们将转换包装在CONVERT_INC函数中。INC1是一个详细的设计实例,提供了转换方法。
转换为Verilog将在文件inc.v中生成一个等效的Verilog模块。
Verilog代码如下所示:

// File: inc.v
// Generated by MyHDL 1.0dev
// Date: Sun May 22 18:46:37 2016


`timescale 1ns/10ps

module inc (
    count,
    enable,
    clock,
    reset
);
// Incrementer with enable.
// 
// count -- output
// enable -- control input, increment when 1
// clock -- clock input
// reset -- asynchronous reset input

output [7:0] count;
reg [7:0] count;
input enable;
input clock;
input reset;



always @(posedge clock, negedge reset) begin: INC_SEQ
    if (reset == 0) begin
        count <= 0;
    end
    else begin
        if (enable) begin
            count <= (count + 1);
        end
    end
end
endmodule

转换器推导出合适的Verilog模块接口,并将MyHDL生成器映射到Verilog Always块。类似地,转换为VHDL会生成包含以下内容的文件inc.vhd:

-- File: inc.vhd
-- Generated by MyHDL 1.0dev
-- Date: Sun May 22 18:46:37 2016


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

use work.pck_myhdl_10.all;

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;
-- Incrementer with enable.
-- 
-- count -- output
-- enable -- control input, increment when 1
-- clock -- clock input
-- reset -- asynchronous reset input

architecture MyHDL of inc is



begin




INC_SEQ: process (clock, reset) is
begin
    if (reset = '0') then
        count <= to_unsigned(0, 8);
    elsif rising_edge(clock) then
        if bool(enable) then
            count <= (count + 1);
        end if;
    end if;
end process INC_SEQ;

end architecture MyHDL;

在这种情况下,MyHDL生成器被映射到VHDL进程。请注意,VHDL文件引用了名为pck_myhdl_的VHDL包。此程序包含许多方便的函数,这些函数使转换变得更容易。还请注意在接口中使用InOut。这不是建议的VHDL设计实践,但在这里需要有一个有效的VHDL设计,以匹配MyHDL设计的行为。由于这只是端口的问题,而且转换器输出是非分层的,因此这个问题不是很常见,并且有一个简单的解决方法。

小的组合电路设计

第二个例子是一个小的组合设计,更具体地说,是前面章节中的二进制到格雷码转换器:

from myhdl import block, always_comb

@block
def bin2gray(B, G):
    """ Gray encoder.

    B -- binary input 
    G -- Gray encoded output
    """

    @always_comb
    def logic():
        G.next = (B>>1) ^ B

    return logic

如前所述,可以创建实例并转换为Verilog和VHDL,如下所示:

from myhdl import Signal, intbv

from bin2gray import bin2gray

def convert(hdl, width=8):

    B = Signal(intbv(0)[width:])
    G = Signal(intbv(0)[width:])

    inst = bin2gray(B, G)
    inst.convert(hdl=hdl)


convert(hdl='Verilog')
convert(hdl='VHDL')

生成的Verilog代码如下所示:

// File: bin2gray.v
// Generated by MyHDL 1.0dev
// Date: Mon May 23 16:09:27 2016
`timescale 1ns/10ps
module bin2gray (
    B,
    G
);
// Gray encoder.
// 
// B -- binary input 
// G -- Gray encoded output
input [7:0] B;
output [7:0] G;
wire [7:0] G;
assign G = ((B >>> 1) ^ B);
endmodule

生成的VHDL代码如下所示:

-- File: bin2gray.vhd
-- Generated by MyHDL 1.0dev
-- Date: Mon May 23 16:09:27 2016

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

use work.pck_myhdl_10.all;

entity bin2gray is
    port (
        B: in unsigned(7 downto 0);
        G: out unsigned(7 downto 0)
    );
end entity bin2gray;
-- Gray encoder.
-- 
-- B -- binary input 
-- G -- Gray encoded output

architecture MyHDL of bin2gray is
begin
G <= (shift_right(B, 1) xor B);
end architecture MyHDL;

分层设计

转换器可以处理任意深层次的设计。
例如,假设我们要设计一个具有格雷码输出的增量器。
使用前面几节中的设计,我们可以继续执行以下操作:

from myhdl import block, Signal, modbv 

from bin2gray import bin2gray
from inc import inc

@block
def gray_inc(graycnt, enable, clock, reset, width):
    
    bincnt = Signal(modbv(0)[width:])
    
    inc_0 = inc(bincnt, enable, clock, reset)
    bin2gray_0 = bin2gray(B=bincnt, G=graycnt)
    
    return inc_0, bin2gray_0

根据格雷码的特性,在连续的值中只有一位会改变。然而,由于bin2Gray模块是组合的,所以输出比特可能会有短暂的毛刺,这可能是不可取的。
为了解决这个问题,让我们创建一个额外的层次结构,并向设计中添加一个输出寄存器。
(这将造成时钟周期的额外延迟,这可能是不可接受的,但我们将在此处忽略这一点。)

from myhdl import block, Signal, modbv 

from bin2gray import bin2gray
from inc import inc

@block
def gray_inc(graycnt, enable, clock, reset, width):
    
    bincnt = Signal(modbv(0)[width:])
    
    inc_0 = inc(bincnt, enable, clock, reset)
    bin2gray_0 = bin2gray(B=bincnt, G=graycnt)
    
    return inc_0, bin2gray_0

我们可以按如下方式转换此分层设计:

from myhdl import Signal, ResetSignal, modbv

from gray_inc_reg import gray_inc_reg

def convert_gray_inc_reg(hdl, width=8):
    graycnt = Signal(modbv(0)[width:])
    enable = Signal(bool())
    clock = Signal(bool())
    reset = ResetSignal(0, active=0, async=True)

    inst = gray_inc_reg(graycnt, enable, clock, reset, width)
    inst.convert(hdl)

convert_gray_inc_reg(hdl='Verilog')
convert_gray_inc_reg(hdl='VHDL')

Verilog输出代码如下所示:

// File: gray_inc_reg.v
// Generated by MyHDL 1.0dev
// Date: Thu Jun 23 19:06:43 2016


`timescale 1ns/10ps

module gray_inc_reg (
    graycnt,
    enable,
    clock,
    reset
);


output [7:0] graycnt;
reg [7:0] graycnt;
input enable;
input clock;
input reset;

wire [7:0] graycnt_comb;
reg [7:0] gray_inc_1_bincnt;



always @(posedge clock, negedge reset) begin: GRAY_INC_REG_GRAY_INC_1_INC_1_SEQ
    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
end
assign graycnt_comb = ((gray_inc_1_bincnt >>> 1) ^ gray_inc_1_bincnt);


always @(posedge clock, negedge reset) begin: GRAY_INC_REG_REG_0
    if (reset == 0) begin
        graycnt <= 0;
    end
    else begin
        graycnt <= graycnt_comb;
    end
end

endmodule

VHDL输出代码如下所示:

-- File: gray_inc_reg.vhd
-- Generated by MyHDL 1.0dev
-- Date: Thu Jun 23 19:06:43 2016


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

use work.pck_myhdl_10.all;

entity gray_inc_reg is
    port (
        graycnt: out unsigned(7 downto 0);
        enable: in std_logic;
        clock: in std_logic;
        reset: in std_logic
    );
end entity gray_inc_reg;


architecture MyHDL of gray_inc_reg is


signal graycnt_comb: unsigned(7 downto 0);
signal gray_inc_1_bincnt: unsigned(7 downto 0);

begin




GRAY_INC_REG_GRAY_INC_1_INC_1_SEQ: process (clock, reset) is
begin
    if (reset = '0') then
        gray_inc_1_bincnt <= to_unsigned(0, 8);
    elsif rising_edge(clock) then
        if bool(enable) then
            gray_inc_1_bincnt <= (gray_inc_1_bincnt + 1);
        end if;
    end if;
end process GRAY_INC_REG_GRAY_INC_1_INC_1_SEQ;


graycnt_comb <= (shift_right(gray_inc_1_bincnt, 1) xor gray_inc_1_bincnt);

GRAY_INC_REG_REG_0: process (clock, reset) is
begin
    if (reset = '0') then
        graycnt <= to_unsigned(0, 8);
    elsif rising_edge(clock) then
        graycnt <= graycnt_comb;
    end if;
end process GRAY_INC_REG_REG_0;

end architecture MyHDL;

请注意,输出是一个平面的“网表列表”,并根据需要生成分层的信号名称。

有限状态机的优化

与通常在硬件设计中一样,有限状态机值得特别注意。
在Verilog和VHDL中,有限状态机通常使用case语句来描述。
Python没有case语句,但是转换器识别特定的if-Then-Else结构并将它们映射到case语句。
当将类型为枚举类型的变量与if-Then-Else结构中的枚举项进行顺序测试时,就会发生此优化。
此外,在Verilog代码中生成了用于适当的高效综合注释。
作为进一步的优化,函数enum得到了增强,使用了额外的参数编码,以优雅地支持不同的编码方案。

例如:

t_State = enum('SEARCH', 'CONFIRM', 'SYNC', encoding='one_hot')

默认的编码是‘二进制’;其他可能的编码是‘one_hot’和‘one_cold’。此参数仅影响转换输出,而不影响类型的行为。
针对case语句生成的Verilog代码进行了优化,以便根据编码进行有效的实现。
请注意,相比之下,Verilog设计器必须进行重要的代码更改才能实现不同的编码方案。
例如,考虑以下有限状态机,其状态变量使用上面定义的枚举类型:

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

转换与以前一样完成:

SOF = Signal(bool(0))
syncFlag = Signal(bool(0))
clk = Signal(bool(0))
reset_n = Signal(bool(1))
state = Signal(t_State.SEARCH)
toVerilog(FramerCtrl, SOF, state, syncFlag, clk, reset_n)
toVHDL(FramerCtrl, SOF, state, syncFlag, clk, reset_n)

Verilog输出如下所示:

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

VHDL输出如下所示:

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;

RAM推断

某些综合工具可以推断RAM结构。
为了支持这一特性,转换器将MyHDL中的信号列表映射到Verilog存储器和VHDL数组。
下面的MyHDL示例是一个RAM模型,它使用一个信号列表来对内部内存进行建模。

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:
            mem[addr].next = din

    @always_comb
    def read():
        dout.next = mem[addr]

    return write, read

使用接口端口的适当信号定义,它将转换为以下Verilog代码。
注意信号mem的列表是如何映射到Verilog内存的。

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

在VHDL中,MyHDL信号列表被建模为VHDL阵列信号:

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

use work.pck_myhdl_06.all;

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;

ROM推断

一些综合工具可以从CASE语句推断ROM内存。
Verilog转换器可以根据更高级别的描述自动扩展为Case语句。
ROM访问是通过索引成整数的元组,在一行中进行描述的。
元组可以手工描述,也可以通过编程方式进行描述。
请注意,使用元组而不是列表来强调内存的只读字符。
下面的示例演示此功能。
ROM访问描述如下:

def rom(dout, addr, CONTENT):

    @always_comb
    def read():
        dout.next = CONTENT[int(addr)]

    return read

ROM内容被描述为整数的元组
定义ROM内容后,可以执行以下转换:

CONTENT = (17, 134, 52, 9)
dout = Signal(intbv(0)[8:])
addr = Signal(intbv(0)[4:])

toVerilog(rom, dout, addr, CONTENT)
toVHDL(rom, dout, addr, CONTENT)

Verilog输出代码如下所示:

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

VHDL输出代码如下所示:

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

begin

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;

用户定义代码

MyHDL提供了一种在转换过程中包含用户定义代码的方法,它使用了特殊的函数属性VHDLCODE和VERILOG_CODE。
例如:

def inc_comb(nextCount, count, n):

    @always(count)
    def logic():
        # do nothing here
        pass

    nextCount.driven = "wire"

    return logic

inc_comb.verilog_code =\
"""
assign $nextCount = ($count + 1) % $n;
"""

inc_comb.vhdl_code =\
"""
$nextCount <= ($count + 1) mod $n;
"""

转换后的代码在Verilog中如下所示:

module inc_comb (
    nextCount,
    count
);

output [7:0] nextCount;
wire [7:0] nextCount;
input [7:0] count;

assign nextCount = (count + 1) % 256;

endmodule

转换后的代码在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;

在此示例中,将绕过Inc._COME函数的转换,而插入用户定义的代码。
用户定义的代码是一个Python模板字符串,它可以通过基于$的替换来引用MyHDL上下文中的信号和参数。
在转换过程中,将替换适当的层次名称和参数值。
MyHDL代码包含以下任务:

nextCount.driven = "wire"

这指定该模块将作为Verilog wire驱动NextCount信号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值