以下文章来源于 OpenFPGA,作者 碎碎思
作为一名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.comaxi_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.comblk_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: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 编写 FPGA 实现脚本,能够轻松简单地定位新的 FPGA 设备。由于 FuseSoC 是一个包管理器和构建系统工具,能够轻松进行IP管理和不同设别之间工程管理。同时该项目中构建了很多IP可以使用。
这里还有一点,就是使用 FuseSoC 可以进行快速验证,因为它还支持一系列仿真工具。
全部0条评论
快来发表一下你的评论吧 !