碎碎思 · 11月12日

多平台FPGA工程快速移植与构建

作为一名FPGA工程师,经常需要在多个FPGA设备之间移植项目,核心的问题是IP的管理和移植,今天通过安装和使用 FuseSoC 在多个 AMD FPGA 之间移植一个简单的项目。从 AMD Spartan™ 7 更改为 AMD Artix™ 7 设备,然后是 AMD Kintex™ UltraSacle™。

FuseSoC 介绍

image.png

FuseSoC 是一款IP管理器和一套用于 HDL(硬件描述语言)代码的构建工具。

其主要目的是增加 IP 核心的重用,有助于创建、构建和仿真 SoC的解决方案。

FuseSoC 具有如下功能:

  • 重复使用现有核心
  • 创建编译时或运行时配置
  • 针对多个仿真器运行回归测试
  • 让其他项目轻松使用你的代码

FuseSoC 最新可扩展版本支持使用 GHDL、Icarus Verilog、Isim、ModelSim、Verilator 和 Xsim 进行仿真。还支持使用 Altera Quartus、IceStorm、Xilinx ISE 和 Xilinx Vivado 构建 FPGA 映像。支持新的 EDA 工具需要大约 100 行代码,并且会不断添加新工具。

FuseSoC 已成功用于构建或仿真 Nyuzi、Pulpino、VScale、OpenRISC SoC、picorv32、osvvm 等项目。

安装 FuseSoC

FuseSoC 以 Python 包的形式提供,因此我们可以使用 pip 安装。对于这个项目,将使用 VSCode 作为安装和使用 FuseSoC 的主要方法。

首先要检查是否安装了 Python

python --version  

image.png

下一步是安装 FuseSoC

pip3 install --upgrade fusesoc  

image.png

要检查 FuseSoC 是否已正确安装,可以运行命令

fusesoc --version  

可以看到类似下面的内容

image.png

FuseSoC 结构

FuseSoC 提供包管理和构建系统功能,因此需要了解一些基本概念才能有效地使用它。

FuseSoC 的关键元素是核心,核心就像我们平时熟知的 HDL IP。核心由 FuseSoC 包管理器进行管理,为了能够管理核心,每个核心都有一个名称和附加信息,这些附加信息在核心文件中提供。

为了 FuseSoC 管理 IP 核,核心文件的扩展名为.core

image.png

FuseSoC 的一个优点是核心可以具有依赖关系,例如,实现图像直方图和通过 AXI 接口的核心可以依赖于实现 AXI 接口的核心。

核心可以存储在本地或远程服务器上。核心的集合称为核心库,核心库最简单的实现是包含多个核心的目录。

FuseSoC 构建系统时能够解决核心依赖关系,就顶层核心而言。它可以是位于 github 或 bitbucket 上的 git repo 上的远程库。

虽然 FuseSoC 构建系统整理了构建设计所需的所有文件,但 AMD Vivado™ Design Suite 中的实际使用 EDAlize。EDALize 抽象了项目创建过程并执行 AMD Vivado™ Design Suite 完成综合、布局和布线以及生成比特流。

我们可以使用顶层的.core文件来整合几个不同的核心库,并控制顶层入口点和最终 FPGA 设计的目标。

image.png

FuseSoC 能够与多个不同的库协同工作,为了向 FuseSoC 提供库的位置,需要使用名为 fusesoc.conf的文件。FuseSoC 将首先在当前工作目录中查找 .conf 文件,如果未找到,它将在主目录 (Linux) 或 Windows %homedirectory% 中查找。

虽然我们可以手动创建此文件,但我们可以使用下面的命令自动创建它。

fusesoc library add /path/to/directory  

image.png

使用 FuseSoC

上面介绍的比较抽象,我们接下来使用一个实例来介绍FuseSoC的使用。

我们将在该项目中使用的源代码是 UART to AXI 逻辑(文末提供)。

针对以下主板:Digilent Arty S7、Digilent Arty A7、Alinx KU040进行相同的工程设计。

首相,创建一个名为 SRC 的核心库,在该库下添加 HDL 元素的三个源文件。

还展示如何使用 AMD Vivado™ Design Suite IP 集成器设计并使用 FuseSoC 构建它们。将在 IP 集成器中包含一些设计元素。这种方法可以被视为一种混合方法,IP 集成器设计将映射到顶层 VHDL 设计中。

由于不想在 AMD Vivado™ Design Suite 中为不同的构建版本创建几个不同的构建元素,所以将创建一个可由 FuseSoC 运行的 tcl 脚本。

该脚本将实例化 AXI BRAM 控制器和 BRAM 连接到自定义 RTL 模块。

# Start a new project or open an existing one in Vivado  
# Open the IP Integrator design tool  
create_bd_design "design_1"  
  
# Add an AXI BRAM Controller  
set axi_bram_ctrl [create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.1 axi_bram_ctrl_0]  
  
# Configure the AXI BRAM Controller for AXI4-Lite interface  
set_property CONFIG.PROTOCOL {AXI4LITE} [get_bd_cells $axi_bram_ctrl]  
  
# Add a Block RAM (BRAM)  
set bram [create_bd_cell -type ip -vlnv xilinx.com:ip:blk_mem_gen:8.4 bram_0]  
  
# Connect the BRAM Controller to the BRAM  
connect_bd_intf_net -intf_net S_AXI $axi_bram_ctrl/BRAM_PORTA $bram/BRAM_PORTA  
  
# Make AXI interface, clock, and reset external  
# Expose the AXI interface to external ports  
make_bd_intf_pins_external [get_bd_intf_pins $axi_bram_ctrl/S_AXI]  
  
# Expose the clock to an external port  
make_bd_pins_external [get_bd_pins $axi_bram_ctrl/s_axi_aclk]  
  
# Expose the reset to an external port  
make_bd_pins_external [get_bd_pins $axi_bram_ctrl/s_axi_aresetn]  
  
# Assign addresses  
assign_bd_address  
  
# Save and validate the design  
validate_bd_design  
save_bd_design  
  
# Generate the HDL wrapper for the design and capture the generated filename  
set wrapper_file [make_wrapper -files [get_files design_1.bd] -top]  
  
# Add the generated wrapper file to the project  
add_files $wrapper_file  
  
# Update the project hierarchy to include the new wrapper file  
update_compile_order -fileset sources_1  

该脚本将创建如下所示的框图。

image.png

然后,将创建一个顶层 RTL 文件,将 IP 集成器框图与自定义 RTL 模块连接起来完成设计。

协议文件

library ieee;  
use ieee.std_logic_1164.all;  
use ieee.numeric_std.all;  
--Declare entity  
entity axi_protocol is  
generic(  
G_AXIL_DATA_WIDTH    :integer := 32; --Width of AXI Lite data bus  
G_AXI_ADDR_WIDTH     :integer := 32; --Width of AXI Lite Address Bu  
G_AXI_ID_WIDTH       :integer := 8; --Width of AXI ID Bus  
G_AXI_AWUSER_WIDTH   :integer := 1 --Width of AXI AW User bus  
);  
port(  
--Master clock & reset  
clk              :in std_ulogic; --System clock  
reset            :in std_ulogic; --System reset, async active low  
--! Master AXIS Interface  
m_axis_tready : in std_logic;  
m_axis_tdata  : out std_logic_vector(7 downto 0);  
m_axis_tvalid : out std_logic;  
--! Slave AXIS Interface  
s_axis_tready : out std_logic;  
s_axis_tdata  : in std_logic_vector(7 downto 0);  
s_axis_tvalid : in std_logic;  
--! AXIL Interface  
--!Write address  
axi_awaddr    : out std_logic_vector(G_AXI_ADDR_WIDTH-1 downto 0);  
axi_awprot    : out std_logic_vector(2 downto 0);  
axi_awvalid   : out std_logic;  
--!write data  
axi_wdata     : out std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);  
axi_wstrb     : out std_logic_vector(G_AXIL_DATA_WIDTH/8-1 downto 0);  
axi_wvalid    : out std_logic;  
--!write response  
axi_bready    : out std_logic;  
--!read address  
axi_araddr    : out std_logic_vector(G_AXI_ADDR_WIDTH-1 downto 0);  
axi_arprot    : out std_logic_vector(2 downto 0);  
axi_arvalid   : out std_logic;  
--!read data  
axi_rready    : out std_logic;  
--write address  
axi_awready   : in std_logic;  
--write data  
axi_wready    : in std_logic;  
--write response  
axi_bresp     : in std_logic_vector(1 downto 0);  
axi_bvalid    : in std_logic;  
--read address  
axi_arready   : in std_logic;  
--read data  
axi_rdata     : in std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);  
axi_rresp     : in std_logic_vector(1 downto 0);  
axi_rvalid    : in std_logic  
);  
end entity axi_protocol;  
architecture rtl of axi_protocol is  
constant C_SINGLE_READ            : std_logic_vector(7 downto 0) := x"05";  
constant C_SINGLE_WRITE           : std_logic_vector(7 downto 0) := x"09";  
constant C_NUMB_ADDR_BYTES        : integer := 4;  
constant C_NUMB_LENGTH_BYTES      : integer := 1;  
constant C_NUMB_DATA_BYTES        : integer := 4;  
constant C_NUMB_AXIL_DATA_BYTES   : integer := 4;  
constant C_NUMB_CRC_BYTES         : integer := 4;  
constant C_MAX_NUMB_BYTES         : integer := 4; -- max number of the above constant for number of bytes  
constant C_ZERO_PAD               : std_logic_vector(7 downto 0) := (others => '0');  
type t_fsm is (idle, address, length, dummy, write_payload, read_payload, crc, write_axil, write_axi, read_axi, read_axil);  
type t_op_fsm is (idle, output, check);  
type t_array is array (0 to 7) of std_logic_vector(31 downto 0);  
type axil_read_fsm is (IDLE, START, CHECK_ADDR_RESP, READ_DATA, DONE);  
type axil_write_fsm is (IDLE, START, CHECK_ADDR_RESP, WRITE_DATA, RESP_READY, CHECK_RESP, DONE);  
signal write_state : axil_write_fsm;  
signal read_state : axil_read_fsm;  
signal s_current_state : t_fsm;  
signal s_command : std_logic_vector(7 downto 0);  
signal s_address : std_logic_vector((C_NUMB_ADDR_BYTES * 8)-1 downto 0);  
signal s_length : std_logic_vector(7 downto 0);  
signal s_length_axi : std_logic_vector(7 downto 0);  
signal s_buf_cnt : unsigned(7 downto 0);  
signal s_byte_pos : integer range 0 to C_MAX_NUMB_BYTES;  
signal s_num_bytes : integer range 0 to C_MAX_NUMB_BYTES;  
signal s_s_tready : std_logic;  
signal s_write_buffer : t_array :=(others=>(others=>'0'));  
signal s_read_buffer : t_array :=(others=>(others=>'0'));  
signal s_write_buffer_temp : std_logic_vector(31 downto 0);  
signal s_read_buffer_temp : std_logic_vector(31 downto 0);  
--axil lite data interface  
signal s_axil_data : std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);  
signal s_axil_valid : std_logic;  
signal s_axil_idata : std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);  
--axi mstream  
signal s_opptr : unsigned(7 downto 0);  
signal s_start : std_logic;  
signal s_op_state : t_op_fsm;  
signal s_op_byte : integer range 0 to C_MAX_NUMB_BYTES;  
signal start_read : std_logic;  
signal start_write : std_logic;  
signal s_m_axis_tvalid : std_logic;  
begin  
s_axis_tready <= s_s_tready;  
FSM : process(clk, reset )  
begin  
if (reset = '0') then  
start_read  <= '0';  
start_write <= '0';  
s_s_tready  <= '0';  
elsif rising_edge(clk) then  
s_s_tready  <= '1';  
s_start     <= '0';  
start_read  <= '0';  
start_write <= '0';  
case s_current_state is  
when idle => -- to do needs to check the command is valid  
s_buf_cnt           <= (others =>'0');  
if (s_axis_tvalid = '1' and s_s_tready = '1') and  
(s_axis_tdata = C_SINGLE_READ  or s_axis_tdata = C_SINGLE_WRITE) then  
s_s_tready <= '0';  
s_command <= s_axis_tdata;  
s_current_state <= address;  
s_byte_pos <= C_NUMB_ADDR_BYTES;  
end if;  
when address =>  
if s_byte_pos = 0 then  
s_s_tready <= '0';  
s_byte_pos <= C_NUMB_LENGTH_BYTES;  
s_current_state <= length;  
elsif s_axis_tvalid = '1' and s_s_tready = '1' then  
s_address <= s_address(s_address'length-8-1 downto 0) & s_axis_tdata;  
s_byte_pos <= s_byte_pos - 1;  
if s_byte_pos = 1 then  
s_s_tready <= '0';  
end if;  
end if;  
when length =>  
if s_byte_pos = 0 then  
s_s_tready <= '0';  
if s_command = C_SINGLE_READ and unsigned(s_length) = 1 then  
s_current_state <= read_axil;  
start_read      <= '1';  
s_num_bytes     <= C_NUMB_AXIL_DATA_BYTES;  
elsif s_command = C_SINGLE_WRITE then  
s_buf_cnt       <= (others =>'0');  
s_byte_pos      <= C_NUMB_AXIL_DATA_BYTES;  
s_num_bytes     <= C_NUMB_AXIL_DATA_BYTES;  
s_current_state <= write_payload;  
end if;  
elsif s_axis_tvalid = '1' and s_s_tready = '1' then  
s_length            <= s_axis_tdata;  
s_length_axi        <= std_logic_vector(unsigned(s_axis_tdata)-1);  
s_byte_pos          <= s_byte_pos - 1;  
s_s_tready <= '0';  
end if;  
when read_axil =>  
if s_axil_valid = '1' then  
s_start             <= '1';  
s_read_buffer(0)(G_AXIL_DATA_WIDTH-1 downto 0) <= s_axil_data;  
end if;  
if (read_state = DONE) then  
s_current_state <= read_payload;  
end if;  
when write_payload =>  
if s_buf_cnt = unsigned(s_length) then  
s_s_tready <= '0';  
s_current_state <= write_axil;  
start_write <= '1';  
else  
if s_byte_pos = 0 then  
s_s_tready <= '0';  
s_byte_pos <= s_num_bytes;  
s_write_buffer(to_integer(s_buf_cnt)) <= s_write_buffer_temp;  
s_buf_cnt <= s_buf_cnt + 1;  
elsif (s_axis_tvalid = '1' and s_s_tready = '1') then  
s_write_buffer_temp <= s_write_buffer_temp(s_write_buffer_temp'length-8-1 downto 0) & s_axis_tdata;  
s_byte_pos <= s_byte_pos - 1;  
if s_byte_pos = 1 then  
s_s_tready <= '0';  
end if;  
end if;  
end if;  
when write_axil =>  
s_s_tready <= '0';  
s_axil_idata <= s_write_buffer(0);  
if (write_state = DONE) then  
s_current_state <= idle;  
end if;  
when read_payload =>  
s_current_state <= idle;  
when others => null;  
end case;  
end if;  
end process;  
m_axis_tvalid <= s_m_axis_tvalid;  
process(clk, reset)  
begin  
if (reset = '0') then  
s_m_axis_tvalid      <= '0';  
m_axis_tdata        <= (others =>'0');  
s_opptr             <= (others => '0');  
s_op_byte           <= C_NUMB_AXIL_DATA_BYTES;  
elsif rising_edge(clk) then  
case s_op_state is  
when idle =>  
s_m_axis_tvalid <= '0';  
if s_start = '1' then  
s_opptr     <= (others => '0');  
s_read_buffer_temp <= s_read_buffer(0);  
s_op_byte   <= s_num_bytes;  
s_op_state  <= output;  
end if;  
when output =>  
if s_opptr = unsigned(s_length) then  
s_op_state <= idle;  
s_m_axis_tvalid <= '0';  
else  
s_m_axis_tvalid <= '1';  
m_axis_tdata <= s_read_buffer_temp(7 downto 0);  
if s_op_byte = 0 then  
s_op_byte   <= s_num_bytes;  
s_opptr     <= s_opptr + 1;  
s_m_axis_tvalid <= '0';  
elsif m_axis_tready = '1' then  
s_m_axis_tvalid <= '1';  
s_read_buffer_temp <= C_ZERO_PAD & s_read_buffer_temp(s_read_buffer_temp'length-1 downto 8);  
s_op_byte <= s_op_byte - 1;  
s_op_state  <= check;  
end if;  
end if;  
when check =>  
s_m_axis_tvalid <= '0';  
s_op_state  <= output;  
end case;  
end if;  
end process;  
process(clk, reset)  
begin  
if (reset = '0') then  
write_state <= IDLE;  
axi_awaddr  <= (others =>'0');  
axi_awprot  <= (others =>'0');  
axi_awvalid <= '0';  
axi_wdata   <= (others =>'0');  
axi_wstrb   <= (others =>'0');  
axi_wvalid  <= '0';  
axi_bready  <= '0';  
elsif rising_edge(clk) then  
axi_wstrb   <= (others =>'0');  
case write_state is  
--Send write address  
when IDLE =>  
if start_write = '1' then  
write_state <= START;  
end if;  
when START =>  
axi_awaddr  <= s_address;  
axi_awprot  <= "010";  
axi_awvalid <= '1';  
axi_wdata   <= s_axil_idata;  
axi_wvalid  <= '1';  
axi_wstrb   <= (others =>'1');  
write_state <= WRITE_DATA;--CHECK_ADDR_RESP;  
--Wait for slave to acknowledge receipt  
when CHECK_ADDR_RESP =>  
if (axi_awready = '1' ) then  
axi_awaddr  <= (others => '0');  
axi_awprot  <= (others => '0');  
axi_awvalid <= '0';  
write_state <= WRITE_DATA;  
else  
write_state <= CHECK_ADDR_RESP;  
end if;  
--Send write data  
when WRITE_DATA =>  
if (axi_awready = '1' ) then  
axi_awaddr  <= (others => '0');  
axi_awprot  <= (others => '0');  
axi_awvalid <= '0';  
axi_wstrb   <= (others =>'0');  
end if;  
axi_wdata  <= s_axil_idata;  
axi_wvalid <= '1';  
axi_wstrb   <= (others =>'1');  
if (axi_wready = '1') then  
write_state <= RESP_READY;  
else  
write_state <= WRITE_DATA;  
end if;  
--Set response ready  
when RESP_READY =>  
axi_wstrb   <= (others =>'0');  
axi_wvalid <= '0';  
axi_bready <= '1';  
write_state <= CHECK_RESP;  
--Check the response  
when CHECK_RESP =>  
if (axi_bvalid = '1') then  
axi_bready <= '0';  
write_state <= DONE;  
end if;  
--Indicate the transaction has completed  
when DONE =>  
write_state <= IDLE;  
when others =>  
write_state <= START;  
end case;  
end if;  
end process;  
process(clk, reset)  
begin  
if (reset = '0') then  
read_state <= IDLE;  
axi_araddr  <= (others =>'0');  
axi_arprot  <= (others =>'0');  
axi_arvalid <= '0';  
axi_rready  <= '0';  
elsif rising_edge(clk) then  
case read_state is  
when IDLE =>  
if start_read = '1' then  
read_state <= START;  
end if;  
--Send read address  
when START =>  
axi_araddr  <= s_address;  
axi_arprot  <= "010";  
axi_arvalid <= '1';  
s_axil_valid <= '0';  
read_state <= CHECK_ADDR_RESP;  
--Wait for the slave to acknowledge receipt of the address  
when CHECK_ADDR_RESP =>  
if (axi_arready = '1' ) then  
axi_araddr  <= (others => '0');  
axi_arprot  <= (others => '0');  
axi_arvalid <= '0';  
read_state <= READ_DATA;  
else  
read_state <= CHECK_ADDR_RESP;  
end if;  
s_axil_valid <= '0';  
--Read data from the slave  
when READ_DATA =>  
s_axil_data  <= axi_rdata;  
if (axi_rvalid = '1') then  
s_axil_valid <= '1';  
read_state <= DONE;  
else  
s_axil_valid <= '0';  
read_state <= READ_DATA;  
end if;  
axi_rready <= '1';  
--Indicate the transaction has completed  
when DONE =>  
axi_rready <= '0';  
s_axil_data  <= (others => '0');  
s_axil_valid <= '0';  
read_state <= IDLE;  
when others =>  
read_state <= START;  
end case;  
end if;  
end process;  
end architecture;  

UART 及 UART 封装

library ieee;  
use ieee.std_logic_1164.all;  
use ieee.numeric_std.all;  
use ieee.math_real.all;  
use work.adiuvo_uart.all;  
entity uart is generic (  
reset_level : std_logic := '0'; -- reset level which causes a reset  
clk_freq    : natural := 100_000_000; -- oscillator frequency  
baud_rate   : natural := 115200 -- baud rate  
);  
port (  
--!System Inputs  
clk   : in std_logic;  
reset : in std_logic;  
--!External Interfaces  
rx : in std_logic;  
tx  : out std_logic;  
--! Master AXIS Interface  
m_axis_tready : in std_logic;  
m_axis_tdata  : out std_logic_vector(7 downto 0);  
m_axis_tvalid : out std_logic;  
--! Slave AXIS Interface  
s_axis_tready : out std_logic;  
s_axis_tdata  : in std_logic_vector(7 downto 0);  
s_axis_tvalid : in std_logic  
);  
end entity;  
architecture rtl of uart is  
constant bit_period : integer := (clk_freq/baud_rate) - 1;  
type cntrl_fsm is (idle, set_tx,wait_tx);  
type rx_fsm is (idle, start, sample, check, wait_axis);  
signal current_state : cntrl_fsm; --:= idle;  
signal rx_state : rx_fsm;-- := idle;  
signal baud_counter : unsigned(vector_size(real(clk_freq), real(baud_rate)) downto 0) := (others => '0'); --timer for outgoing signals  
signal baud_en : std_logic := '0';  
signal meta_reg : std_logic_vector(3 downto 0) := (others => '0'); -- fe detection too  
signal capture : std_logic_vector(7 downto 0) := (others => '0'); -- data and parity  
signal bit_count : integer range 0 to 1023 := 0;  
signal pos_count : integer range 0 to 15 := 0;  
signal running : std_logic := '0';  
signal load_tx : std_logic := '0';  
signal complete : std_logic := '0';  
signal tx_reg : std_logic_vector(11 downto 0) := (others => '0');  
signal tmr_reg : std_logic_vector(11 downto 0) := (others => '0');  
signal payload : std_logic_vector(7 downto 0) := (others => '0');  
constant zero  : std_logic_vector(tmr_reg'range) := (others => '0');  
begin  
process (reset, clk)  
begin  
if reset = reset_level then  
current_state <= idle;  
payload       <= (others => '0');  
load_tx <= '0';  
elsif rising_edge(clk) then  
load_tx <= '0';  
case current_state is  
when idle =>  
if s_axis_tvalid = '1' then  
current_state <= set_tx;  
load_tx       <= '1';  
payload       <= s_axis_tdata;  
end if;  
when set_tx =>  
current_state <= wait_tx;  
when wait_tx =>  
if complete = '1' then  
current_state <= idle;  
end if;  
when others =>  
current_state <= idle;  
end case;  
end if;  
end process;  
s_axis_tready <= '1' when (current_state = idle) else '0';  
process (reset, clk)  
--! baud counter for output TX  
begin  
if reset = reset_level then  
baud_counter <= (others => '0');  
baud_en      <= '0';  
elsif rising_edge(clk) then  
baud_en <= '0';  
if (load_tx = '1') then  
baud_counter <= (others => '0');  
elsif (baud_counter = bit_period) then  
baud_en      <= '1';  
baud_counter <= (others => '0');  
else  
baud_counter <= baud_counter + 1;  
end if;  
end if;  
end process;  
process (reset, clk)  
--!metastability protection rx signal  
begin  
if reset = reset_level then  
meta_reg <= (others => '1');  
elsif rising_edge(clk) then  
meta_reg <= meta_reg(meta_reg'high - 1 downto meta_reg'low) & rx;  
end if;  
end process;  
process (reset, clk)  
begin  
if reset = reset_level then  
pos_count <= 0;  
bit_count <= 0;  
capture     <= (others => '0');  
rx_state    <= idle;  
m_axis_tvalid <= '0';  
m_axis_tdata     <= (others => '0');  
elsif rising_edge(clk) then  
case rx_state is  
when idle =>  
m_axis_tvalid  <= '0';  
if meta_reg(meta_reg'high downto meta_reg'high - 1) = fe_det then  
pos_count <= 0;  
bit_count <= 0;  
capture  <= (others => '0');  
rx_state <= start;  
end if;  
when start =>  
if bit_count = bit_period then  
bit_count <= 0;  
rx_state  <= sample;  
else  
bit_count <= bit_count + 1;  
end if;  
when sample =>  
bit_count <= bit_count + 1;  
rx_state  <= sample;  
if bit_count = (bit_period/2) and (pos_count < 8) then  
capture <= meta_reg(meta_reg'high) & capture(capture'high downto capture'low + 1);  
elsif bit_count = bit_period then  
if pos_count = 8 then  
rx_state <= check;  
else  
pos_count <= pos_count + 1;  
bit_count <= 0;  
end if;  
end if;  
when check =>  
if parity(capture) = '1' then  
m_axis_tvalid <= '1';  
m_axis_tdata  <= capture(7 downto 0);  
rx_state      <= wait_axis;  
else  
m_axis_tvalid <= '1';  
m_axis_tdata  <= capture(7 downto 0);  
rx_state      <= wait_axis;  
end if;  
when wait_axis =>  
if m_axis_tready = '1' then  
m_axis_tvalid <= '0';  
rx_state      <= idle;  
end if;  
end case;  
end if;  
end process;  
op_uart : process (reset, clk)  
begin  
if reset = reset_level then  
tx_reg  <= (others => '1');  
tmr_reg <= (others => '0');  
elsif rising_edge(clk) then  
if load_tx = '1' then  
tx_reg  <= stop_bit & not(parity(payload)) & payload & start_bit ;  
tmr_reg <= (others => '1');  
elsif baud_en = '1' then  
tx_reg  <= '1' & tx_reg(tx_reg'high downto tx_reg'low + 1);  
tmr_reg <= tmr_reg(tmr_reg'high - 1 downto tmr_reg'low) & '0';  
end if;  
end if;  
end process;  
tx       <= tx_reg(tx_reg'low);  
complete <= '1' when (tmr_reg = zero and current_state = wait_tx) else '0';  
end architecture;  
library ieee;  
use ieee.std_logic_1164.all;  
use ieee.numeric_std.all;  
use ieee.math_real.all;  
package adiuvo_uart is  
function vector_size(clk_freq, baud_rate : real) return integer;  
function parity (a : std_logic_vector) return std_logic;  
constant fe_det     : std_logic_vector(1 downto 0) := "10";  
constant start_bit  : std_logic := '0';  
constant stop_bit   : std_logic_vector := "11";  
end package;  
package body adiuvo_uart is  
function vector_size(clk_freq, baud_rate : real) return integer is  
variable div : real;  
variable res : real;  
begin  
div := (clk_freq/baud_rate);  
res := CEIL(LOG(div)/LOG(2.0));  
return integer(res - 1.0);  
end;  
function parity (a : std_logic_vector) return std_logic is  
variable y : std_logic := '0';  
begin  
for i in a'range loop  
y := y xor a(i);  
end loop;  
return y;  
end parity;  
end package body adiuvo_uart;  

TOP模块

LIBRARY ieee;  
USE ieee.std_logic_1164.all;  
USE ieee.numeric_std.all;  
entity top_level is  
port(  
clk   : in std_logic;  
reset : in std_logic;  
rx    : in std_logic;  
tx    : out std_logic  
);  
-- Declarations  
end entity top_level ;  
  
LIBRARY ieee;  
USE ieee.std_logic_1164.all;  
USE ieee.numeric_std.all;  
library UNISIM;  
use UNISIM.VCOMPONENTS.ALL;  
use ieee.math_real.all;  
architecture struct of top_level is  
-- Architecture declarations  
-- Internal signal declarations  
signal S_AXI_0_arready : STD_LOGIC;  
signal S_AXI_0_awready : STD_LOGIC;  
signal S_AXI_0_bresp : STD_LOGIC_VECTOR( 1 downto 0 );  
signal S_AXI_0_bvalid : STD_LOGIC;  
signal S_AXI_0_rdata : STD_LOGIC_VECTOR( 31 downto 0 );  
signal S_AXI_0_rresp : STD_LOGIC_VECTOR( 1 downto 0 );  
signal S_AXI_0_wready : STD_LOGIC;  
signal S_AXI_0_wvalid : STD_LOGIC;  
signal axi_araddr : std_logic_vector(31 downto 0);  
signal axi_arprot : std_logic_vector(2 downto 0);  
signal axi_arvalid : std_logic;  
signal axi_awaddr : std_logic_vector(31 downto 0);  
signal axi_awprot : std_logic_vector(2 downto 0);  
signal axi_awvalid : std_logic;  
signal axi_bready : std_logic;  
signal axi_rready : std_logic;  
signal axi_rvalid : std_logic;  
signal axi_wdata : std_logic_vector(31 downto 0);  
signal axi_wstrb : std_logic_vector(3 downto 0);  
signal m_axis_tdata : std_logic_vector(7 downto 0);  
signal m_axis_tready : std_logic;  
signal m_axis_tvalid : std_logic;  
signal s_axis_tdata : std_logic_vector(7 downto 0);  
signal s_axis_tready : std_logic;  
signal s_axis_tvalid : std_logic;  
-- Component Declarations  
component axi_protocol  
generic (  
G_AXIL_DATA_WIDTH  : integer := 32; --Width of AXI Lite data bus  
G_AXI_ADDR_WIDTH   : integer := 32; --Width of AXI Lite Address Bu  
G_AXI_ID_WIDTH     : integer := 8; --Width of AXI ID Bus  
G_AXI_AWUSER_WIDTH : integer := 1 --Width of AXI AW User bus  
);  
port (  
axi_arready   : in std_logic;  
axi_awready   : in std_logic;  
axi_bresp     : in std_logic_vector (1 downto 0);  
axi_bvalid    : in std_logic;  
axi_rdata     : in std_logic_vector (31 downto 0);  
axi_rresp     : in std_logic_vector (1 downto 0);  
axi_rvalid    : in std_logic;  
axi_wready    : in std_logic;  
clk           : in std_ulogic;  
m_axis_tready : in std_logic;  
reset         : in std_ulogic;  
s_axis_tdata  : in std_logic_vector (7 downto 0);  
s_axis_tvalid : in std_logic;  
axi_araddr    : out std_logic_vector (31 downto 0);  
axi_arprot    : out std_logic_vector (2 downto 0);  
axi_arvalid   : out std_logic;  
axi_awaddr    : out std_logic_vector (31 downto 0);  
axi_awprot    : out std_logic_vector (2 downto 0);  
axi_awvalid   : out std_logic;  
axi_bready    : out std_logic;  
axi_rready    : out std_logic;  
axi_wdata     : out std_logic_vector (31 downto 0);  
axi_wstrb     : out std_logic_vector (3 downto 0);  
axi_wvalid    : out std_logic;  
m_axis_tdata  : out std_logic_vector (7 downto 0);  
m_axis_tvalid : out std_logic;  
s_axis_tready : out std_logic  
);  
end component axi_protocol;  
component design_1_wrapper  
port (  
S_AXI_0_araddr  : in STD_LOGIC_VECTOR ( 11 downto 0 );  
S_AXI_0_arprot  : in STD_LOGIC_VECTOR ( 2 downto 0 );  
S_AXI_0_arvalid : in STD_LOGIC;  
S_AXI_0_awaddr  : in STD_LOGIC_VECTOR ( 11 downto 0 );  
S_AXI_0_awprot  : in STD_LOGIC_VECTOR ( 2 downto 0 );  
S_AXI_0_awvalid : in STD_LOGIC;  
S_AXI_0_bready  : in STD_LOGIC;  
S_AXI_0_rready  : in STD_LOGIC;  
S_AXI_0_wdata   : in STD_LOGIC_VECTOR ( 31 downto 0 );  
S_AXI_0_wstrb   : in STD_LOGIC_VECTOR ( 3 downto 0 );  
S_AXI_0_wvalid  : in STD_LOGIC;  
s_axi_aclk_0    : in STD_LOGIC;  
s_axi_aresetn_0 : in STD_LOGIC;  
S_AXI_0_arready : out STD_LOGIC;  
S_AXI_0_awready : out STD_LOGIC;  
S_AXI_0_bresp   : out STD_LOGIC_VECTOR ( 1 downto 0 );  
S_AXI_0_bvalid  : out STD_LOGIC;  
S_AXI_0_rdata   : out STD_LOGIC_VECTOR ( 31 downto 0 );  
S_AXI_0_rresp   : out STD_LOGIC_VECTOR ( 1 downto 0 );  
S_AXI_0_rvalid  : out STD_LOGIC;  
S_AXI_0_wready  : out STD_LOGIC  
);  
end component design_1_wrapper;  
component uart  
generic (  
reset_level : std_logic := '0'; -- reset level which causes a reset  
clk_freq    : natural := 100_000_000; -- oscillator frequency  
baud_rate   : natural := 115200 -- baud rate  
);  
port (  
clk           : in std_logic;  
m_axis_tready : in std_logic;  
reset         : in std_logic;  
rx            : in std_logic;  
s_axis_tdata  : in std_logic_vector (7 downto 0);  
s_axis_tvalid : in std_logic;  
m_axis_tdata  : out std_logic_vector (7 downto 0);  
m_axis_tvalid : out std_logic;  
s_axis_tready : out std_logic;  
tx            : out std_logic  
);  
end component uart;  
-- Optional embedded configurations  
-- pragma synthesis_off  
for all : axi_protocol use entity src.axi_protocol;  
for all : design_1_wrapper use entity src.design_1_wrapper;  
for all : uart use entity src.uart;  
-- pragma synthesis_on  
begin  
-- Instance port mappings.  
U_0 : axi_protocol  
generic map (  
G_AXIL_DATA_WIDTH => 32, --Width of AXI Lite data bus  
G_AXI_ADDR_WIDTH => 32, --Width of AXI Lite Address Bu  
G_AXI_ID_WIDTH => 1, --Width of AXI ID Bus  
G_AXI_AWUSER_WIDTH => 1 --Width of AXI AW User bus  
)  
port map (  
clk => clk,  
reset => reset,  
m_axis_tready => m_axis_tready,  
m_axis_tdata => m_axis_tdata,  
m_axis_tvalid => m_axis_tvalid,  
s_axis_tready => s_axis_tready,  
s_axis_tdata => s_axis_tdata,  
s_axis_tvalid => s_axis_tvalid,  
axi_awaddr => axi_awaddr,  
axi_awprot => axi_awprot,  
axi_awvalid => axi_awvalid,  
axi_wdata => axi_wdata,  
axi_wstrb => axi_wstrb,  
axi_wvalid => S_AXI_0_wvalid,  
axi_bready => axi_bready,  
axi_araddr => axi_araddr,  
axi_arprot => axi_arprot,  
axi_arvalid => axi_arvalid,  
axi_rready => axi_rready,  
axi_awready => S_AXI_0_wready,  
axi_wready => S_AXI_0_awready,  
axi_bresp => S_AXI_0_bresp,  
axi_bvalid => S_AXI_0_bvalid,  
axi_arready => S_AXI_0_arready,  
axi_rdata => S_AXI_0_rdata,  
axi_rresp => S_AXI_0_rresp,  
axi_rvalid => axi_rvalid  
);  
U_1 : design_1_wrapper  
port map (  
S_AXI_0_araddr => axi_araddr(11 downto 0),  
S_AXI_0_arprot => axi_arprot,  
S_AXI_0_arready => S_AXI_0_arready,  
S_AXI_0_arvalid => axi_arvalid,  
S_AXI_0_awaddr => axi_awaddr(11 downto 0),  
S_AXI_0_awprot => axi_awprot,  
S_AXI_0_awready => S_AXI_0_awready,  
S_AXI_0_awvalid => axi_awvalid,  
S_AXI_0_bready => axi_bready,  
S_AXI_0_bresp => S_AXI_0_bresp,  
S_AXI_0_bvalid => S_AXI_0_bvalid,  
S_AXI_0_rdata => S_AXI_0_rdata,  
S_AXI_0_rready => axi_rready,  
S_AXI_0_rresp => S_AXI_0_rresp,  
S_AXI_0_rvalid => axi_rvalid,  
S_AXI_0_wdata => axi_wdata,  
S_AXI_0_wready => S_AXI_0_wready,  
S_AXI_0_wstrb => axi_wstrb,  
S_AXI_0_wvalid => S_AXI_0_wvalid,  
s_axi_aclk_0 => clk,  
s_axi_aresetn_0 => reset  
);  
U_2 : uart  
generic map (  
reset_level => '0', -- reset level which causes a reset  
clk_freq => 100_000_000, -- oscillator frequency  
baud_rate => 115200 -- baud rate  
)  
port map (  
clk => clk,  
reset => reset,  
rx => rx,  
tx => tx,  
m_axis_tready => s_axis_tready,  
m_axis_tdata => s_axis_tdata,  
m_axis_tvalid => s_axis_tvalid,  
s_axis_tready => m_axis_tready,  
s_axis_tdata => m_axis_tdata,  
s_axis_tvalid => m_axis_tvalid  
);  
end architecture struct;  

创建 XDC

将要进行的三个工程之间的唯一区别在于约束文件。需要为每个目标板创建一个约束。

AMD Spartan™ 7

set_property PACKAGE_PIN        R2         [get_ports clk]  
set_property IOSTANDARD LVCMOS33    [get_ports clk]  
create_clock -period 10.000 -name sys_clk [get_ports clk]  
set_property PACKAGE_PIN    L17                 [get_ports reset]  
set_property PACKAGE_PIN    L18                [get_ports rx]  
set_property PACKAGE_PIN    M14                [get_ports tx]  
# set I/O standard  
set_property IOSTANDARD LVCMOS33                [get_ports reset]  
set_property IOSTANDARD LVCMOS33                [get_ports rx]  
set_property IOSTANDARD LVCMOS33                [get_ports tx]  

AMD Artix™ 7

set_property PACKAGE_PIN        E3         [get_ports clk]  
set_property IOSTANDARD LVCMOS33    [get_ports clk]  
create_clock -period 10.000 -name sys_clk [get_ports clk]  
set_property PACKAGE_PIN    G13                 [get_ports reset]  
set_property PACKAGE_PIN    B11                [get_ports rx]  
set_property PACKAGE_PIN    A11                [get_ports tx]  
# set I/O standard  
set_property IOSTANDARD LVCMOS33                [get_ports reset]  
set_property IOSTANDARD LVCMOS33                [get_ports rx]  
set_property IOSTANDARD LVCMOS33                [get_ports tx]  

AMD Kintex™ UltraSacle™

set_property PACKAGE_PIN        AF9         [get_ports clk]  
set_property IOSTANDARD LVCMOS33    [get_ports clk]  
create_clock -period 10.000 -name sys_clk [get_ports clk]  
set_property PACKAGE_PIN    AE8                [get_ports reset]  
set_property PACKAGE_PIN    AE10               [get_ports rx]  
set_property PACKAGE_PIN    AD10               [get_ports tx]  
# set I/O standard  
set_property IOSTANDARD LVCMOS33                [get_ports reset]  
set_property IOSTANDARD LVCMOS33                [get_ports rx]  
set_property IOSTANDARD LVCMOS33                [get_ports tx]  

创建 FuseSoC 核心

创建 RTL 和XDC后,下一步是创建.core 文件和.conf 文件。

首先要做的是创建.core 文件,它将被分成几个部分,第一部分是定义 CAPI 版本和核心库,提供其名称和描述

CAPI=2:  
  
name: adiuvo::hackster:0.1  
description: Implementation for Hackster Project  

下一步是创建文件集,这些文件集被分成几个不同的组。将其中第一个组命名为核心组,这些文件是所有工程中通用的。

对于每个文件,我们还定义了库和文件类型,在本例中为 vhdl。

下一步是定义创建 IP 集成器设计的 tcl 脚本。由于三个目标板之间的配置没有差异。此文件在所有实现中也是通用的。

如果我们正在创建需要特定电路板配置的 Zynq 或 Zynq MPSoC 设计,我们将需要为定义 PS 配置的每个电路板提供文件的变体。

下一个文件集是 IO 约束,每个所需的目标板都有一个文件集。

filesets:  
  core:  
    files:  
      - src/protocol.vhd : {logical_name: work}  
      - src/uart_pkg.vhd : {logical_name: work}  
      - src/uart.vhd            : {logical_name: work}  
      - src/top_level.vhd       : {logical_name: work}  
    file_type: vhdlSource  
  
  vivado_files_tcl:  
    files:  
      - src/build_ip.tcl: {file_type: tclSource}  
  
  artix_io:  
    files:  
      - constraints/artix7.xdc : {file_type : xdc}  
        
  kintex_io:  
    files:  
      - constraints/kintexus.xdc : {file_type : xdc}  
  
  spartan_io:  
    files:  
      - constraints/spartan.xdc : {file_type : xdc}  

最后一步是定义目标,在这里定义一个包含核心文件集的默认目标。然后再定义三个目标,每个目标板一个。对于每个目标,将工具定义为 AMD Vivado™ Design Suite,并附加该特定目标所需的文件集。

在这种情况下,它是 IO 文件集和 tcl 脚本,用于演示如果每个目标不同,如何使用 TCL 脚本。

对于每个目标,还需要在 AMD Vivado™ Design Suite 中定义顶层模块,当然还有目标设备。

targets:  
  default: &default  
    filesets : [core]  
  
  artix7:  
    <<: *default  
    default_tool: vivado  
    filesets_append : [vivado_files_tcl, artix_io]  
    toplevel : top_level  
    tools:  
      vivado:  
        part : XC7A35TI-CSG324-1L  
  
  spartan7:  
    <<: *default  
    default_tool: vivado  
    filesets_append : [vivado_files_tcl, spartan_io]  
    toplevel : top_level  
    tools:  
      vivado:  
        part : xc7s50-csga324-1  
  
  kintexus:  
    <<: *default  
    default_tool: vivado  
    filesets_append : [vivado_files_tcl, kintex_io]  
    toplevel : top_level  
    tools:  
      vivado:  
        part : xcku040-ffva1156-2-i  

.core 文件完成后,可以仔细检查是否能够看到刚刚定义的库。

下一步是定义 fusesoc.conf 文件,定义核心的位置

[library.hackster]  
location = C:/hdl_projects/hackster_fusesoc  
sync-uri = C:/hdl_projects/hackster_fusesoc/  
sync-type = local  
auto-sync = false  

因为在这个例子中我们只有一个名为 Hackster 的核心库。

我们可以使用命令来检查

fusesoc core list  

可以得到以下输出

image.png

FuseSoC 结果

创建源代码后,可以通过运行命令来构建这三个工程

AMD Artix™ 7

fusesoc --verbose run --target=artix7 --no-export hackster  

AMD Kintex™ UltraSacle™

fusesoc --verbose run --target=kintexus--no-export hackster 

AMD Spartan™ 7

fusesoc --verbose run --target=spartan7--no-export hackster 

随着项目的构建,将看到实施过程的记录滚动过去。

image.png

一旦完成后,将看到一条消息,显示比特流生成已完成。

image.png

代码

FuseSoC

https://fusesoc.net/
https://github.com/fusesoc/fu...

参考工程

https://github.com/ATaylorCEn...

总结

该项目概述了如何使用 FuseSoC 编写 FPGA 实现脚本,能够轻松简单地定位新的 FPGA 设备。由于 FuseSoC 是一个包管理器和构建系统工具,能够轻松进行IP管理和不同设别之间工程管理。同时该项目中构建了很多IP可以使用。

这里还有一点,就是使用 FuseSoC 可以进行快速验证,因为它还支持一系列仿真工具。

END

作者:碎碎思
来源:OpenFPGA

相关文章推荐

更多FPGA干货请关注FPGA的逻辑技术专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
10613
内容数
577
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息