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

情感   2024-11-20 07:43   河北  

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

FuseSoC 介绍

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

下一步是安装 FuseSoC

pip3 install --upgrade fusesoc

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

fusesoc --version

可以看到类似下面的内容

FuseSoC 结构

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

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

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

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

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

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

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

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

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

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

fusesoc library add /path/to/directory

使用 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

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

然后,将创建一个顶层 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

可以得到以下输出

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

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

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

代码

FuseSoC

https://fusesoc.net/

https://github.com/fusesoc/fusesoc-cores

参考工程

https://github.com/ATaylorCEngFIET/hackster_fusesoc

总结

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

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

END


往期精选 

 
 

【免费】FPGA工程师招聘平台

ISE 14.7 安装教程及详细说明

Vivado 2019.2 安装教程

SANXIN-B01开发板verilog教程V3电子版

学员笔记连载 | FPGA Zynq 千兆以太网回环

求职面试 | FPGA或IC面试题最新汇总篇

FPGA项目开发:204B实战应用-LMK04821代码详解(二)

项目合作 | 承接FPGA项目公告

资料汇总|FPGA软件安装包、书籍、源码、技术文档…(2024.07.29更新)


FPGA技术江湖广发江湖帖

无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。


FPGA技术江湖微信交流群

加群主微信,备注姓名+公司/学校+岗位/专业进群


FPGA技术江湖QQ交流群

备注姓名+公司/学校+岗位/专业进群

FPGA技术江湖
任何技术的学习就好比一个江湖,对于每一位侠客都需要不断的历练,从初入江湖的小白到归隐山林的隐世高人,需要不断的自我感悟自己修炼,让我们一起仗剑闯FPGA乃至更大的江湖。
 最新文章