Fun with FuseSoC - Hackster.io
Hardware componentsDigilent Arty S7-50×1Buy from NewarkBuy from digilent.comBuy from AvnetBuy from store.digilentinc.comDigilent Arty A7: Artix-7 FPGA Development Board×1Digilent Cmod S7: Breadboardable Spartan-7 FPGA Module×1Digilent Cmod A7-35T: Breadboardable Artix-7 FPGA Module×1krtkl snickerdoodle×1Avnet MicroZed×1Avnet ZUBoard 1CG×1Software apps and online servicesAMD Vivado Design SuiteFuseSoc
Hardware componentsDigilent Arty S7-50×1Buy from NewarkBuy from digilent.comBuy from AvnetBuy from store.digilentinc.comDigilent Arty A7: Artix-7 FPGA Development Board×1Digilent Cmod S7: Breadboardable Spartan-7 FPGA Module×1Digilent Cmod A7-35T: Breadboardable Artix-7 FPGA Module×1krtkl snickerdoodle×1Avnet MicroZed×1Avnet ZUBoard 1CG×1Software apps and online servicesAMD Vivado Design SuiteFuseSoc
Introduction One of the things you quickly learn as a professional FPGA engineer is the GUI is rarely used in development.Scripting is used for several reasons however, the key reasons areConsistency - Scripts enforces a consistent configuration across the runs and environment. Version Control - Scripting enables easy integration with version control tools Automation - Scripting enables integration with Continuous Integration. In this project we are going to be examining a build system and package manager called FuseSoC. FuseSoC is intended to encourage reuse of IP, along with easing the build and simulation of FPGA based solutions. One of the key aspects of FuseSoC is its ability to rapidly change target devices for example from a AMD Spartan™ 7 to a AMD Artix™ 7 device and then AMD Kintex™ UltraSacle™. Lets take a look at how we can install and work with FuseSoC to port a simple project between several AMD FPGAs.Installing Use SoC FuseSoC is provided as a python package, as such we can install it using pip. For this project I will be using VSCode as my main method of installing and working with FuseSoC.The first thing to do is to, check we have python installed
python --version The next step is to install FuseSoC
pip3 install --upgrade fusesoc To check FuseSoC has been installed correctly we can run the command
fusesoc --versionWe should see similar to below FuseSoC Structure FuseSoC provides package management and a build system, as such there some fundamental concepts we need to understand to work effectively with it.The key element of FuseSoC is the core, cores are as one would expect HDL IP. Cores are discovered by the FuseSoC package manager, to enable discovery of the core each core has a name and other information provided in what is called a core file. To help FuseSoC discover the IP cores, the core file has the extension .coreExample Core file One nice thing about FuseSoC is that cores can have dependencies, for example a core which implements an image histogram and interfaces via AXI could be dependent on a core which implements the AXI interfaces.Cores can be stored either locally or remotely. Collections of cores are called core libraries, the simplest implementation of a core library is a directory containing several cores. The FuseSoC build system is able to resolve core dependencies, with respect the top level core. It can also be a remote library located on a git repo on github or bitbucket for example.Of course building FPGA takes more than just the IP cores as such we need to be able to include constraints files and other files also e.g. IP Integrator block diagrams.While the FuseSoC build system, collates all of the files required to build the design the actual implementation in AMD Vivado™ Design Suite uses EDAlize. EDALize abstracts away the project creation process and executes AMD Vivado™ Design Suite to run through synthesis, place & route and bitstream generation.We can use a .core file at the top level to pull together several different core libraries and control the top level entry point and targeting of the final FPGA design for either implementation of simulation. Example .core file for a top level FuseSoC is capable of working with several different libraries, to provide FuseSoC the location of the libraries a file called fusesoc.conf is used. FuseSoC will first look for the.conf file in the current working directory, alternatively if none is found it will look in either home directory (Linux) or Windows %homedirectory%. While we can create this file manually we can use the command, below to create it automatically.
fusesoc library add /path/to/directoryExample .conf file Using FuseSoC Lets use FuseSoC to create a simple FPGA design which can be deployed across several different AMD devices.The source code we will use for this project is the UART to AXI Network logic, demonstrated in this project. I intend to target the following boards, Digilent Arty S7, Digilent Arty A7, Alinx KU040.To do this we will be creating one core library called SRC under which I am adding in the three source files for the HDL elements.As I want to also show how we can use AMD Vivado™ Design Suite IP integrator designs and build them with FuseSoC. I am going to include some elements of the design in IP integrator. This approach can be considered a hybrid approach, the IP integrator design will be mapped into a top level VHDL design.As I do not want to create several different build elements in AMD Vivado™ Design Suite for different build versions I am going to create a tcl script which can be run by FuseSoC.This script will instantiate the AXI BRAM controller and BRAM to be connected to the custom RTL modules.
# Start a new project or open an existing one in Vivado# Open the IP Integrator design toolcreate_bd_design "design_1"# Add an AXI BRAM Controllerset 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 interfaceset_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 BRAMconnect_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 portsmake_bd_intf_pins_external [get_bd_intf_pins $axi_bram_ctrl/S_AXI]# Expose the clock to an external portmake_bd_pins_external [get_bd_pins $axi_bram_ctrl/s_axi_aclk]# Expose the reset to an external portmake_bd_pins_external [get_bd_pins $axi_bram_ctrl/s_axi_aresetn]# Assign addressesassign_bd_address# Save and validate the designvalidate_bd_designsave_bd_design# Generate the HDL wrapper for the design and capture the generated filenameset wrapper_file [make_wrapper -files [get_files design_1.bd] -top]# Add the generated wrapper file to the projectadd_files $wrapper_file# Update the project hierarchy to include the new wrapper fileupdate_compile_order -fileset sources_1This script will create a block diagram as below I will then create a top level RTL file which connects the IP integrator block diagram with the custom RTL modules to complete the design.The custom RTL modules are provided here and also attached to the end of the project along with all of the files.Protocol File
library ieee;use ieee.std_logic_1164.all;use ieee.numeric_std.all;--Declare entityentity axi_protocol isgeneric(G_AXIL_DATA_WIDTH :integer := 32; --Width of AXI Lite data busG_AXI_ADDR_WIDTH :integer := 32; --Width of AXI Lite Address BuG_AXI_ID_WIDTH :integer := 8; --Width of AXI ID BusG_AXI_AWUSER_WIDTH :integer := 1 --Width of AXI AW User bus);port(--Master clock & resetclk :in std_ulogic; --System clockreset :in std_ulogic; --System reset, async active low--! Master AXIS Interfacem_axis_tready : in std_logic;m_axis_tdata : out std_logic_vector(7 downto 0);m_axis_tvalid : out std_logic;--! Slave AXIS Interfaces_axis_tready : out std_logic;s_axis_tdata : in std_logic_vector(7 downto 0);s_axis_tvalid : in std_logic;--! AXIL Interface--!Write addressaxi_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 dataaxi_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 responseaxi_bready : out std_logic;--!read addressaxi_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 dataaxi_rready : out std_logic;--write addressaxi_awready : in std_logic;--write dataaxi_wready : in std_logic;--write responseaxi_bresp : in std_logic_vector(1 downto 0);axi_bvalid : in std_logic;--read addressaxi_arready : in std_logic;--read dataaxi_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 isconstant 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 bytesconstant 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 interfacesignal 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 mstreamsignal 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;begins_axis_tready <= s_s_tready;FSM : process(clk, reset )beginif (reset = '0') thenstart_read <= '0';start_write <= '0';s_s_tready <= '0';elsif rising_edge(clk) thens_s_tready <= '1';s_start <= '0';start_read <= '0';start_write <= '0';case s_current_state iswhen idle => -- to do needs to check the command is valids_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) thens_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 thens_s_tready <= '0';s_byte_pos <= C_NUMB_LENGTH_BYTES;s_current_state <= length;elsif s_axis_tvalid = '1' and s_s_tready = '1' thens_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 thens_s_tready <= '0';end if;end if;when length =>if s_byte_pos = 0 thens_s_tready <= '0';if s_command = C_SINGLE_READ and unsigned(s_length) = 1 thens_current_state <= read_axil;start_read <= '1';s_num_bytes <= C_NUMB_AXIL_DATA_BYTES;elsif s_command = C_SINGLE_WRITE thens_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' thens_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' thens_start <= '1';s_read_buffer(0)(G_AXIL_DATA_WIDTH-1 downto 0) <= s_axil_data;end if;if (read_state = DONE) thens_current_state <= read_payload;end if;when write_payload =>if s_buf_cnt = unsigned(s_length) thens_s_tready <= '0';s_current_state <= write_axil;start_write <= '1';elseif s_byte_pos = 0 thens_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') thens_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 thens_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) thens_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)beginif (reset = '0') thens_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) thencase s_op_state iswhen idle =>s_m_axis_tvalid <= '0';if s_start = '1' thens_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) thens_op_state <= idle;s_m_axis_tvalid <= '0';elses_m_axis_tvalid <= '1';m_axis_tdata <= s_read_buffer_temp(7 downto 0);if s_op_byte = 0 thens_op_byte <= s_num_bytes;s_opptr <= s_opptr + 1;s_m_axis_tvalid <= '0';elsif m_axis_tready = '1' thens_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)beginif (reset = '0') thenwrite_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) thenaxi_wstrb <= (others =>'0');case write_state is--Send write addresswhen IDLE =>if start_write = '1' thenwrite_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 receiptwhen CHECK_ADDR_RESP =>if (axi_awready = '1' ) thenaxi_awaddr <= (others => '0');axi_awprot <= (others => '0');axi_awvalid <= '0';write_state <= WRITE_DATA;elsewrite_state <= CHECK_ADDR_RESP;end if;--Send write datawhen WRITE_DATA =>if (axi_awready = '1' ) thenaxi_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') thenwrite_state <= RESP_READY;elsewrite_state <= WRITE_DATA;end if;--Set response readywhen RESP_READY =>axi_wstrb <= (others =>'0');axi_wvalid <= '0';axi_bready <= '1';write_state <= CHECK_RESP;--Check the responsewhen CHECK_RESP =>if (axi_bvalid = '1') thenaxi_bready <= '0';write_state <= DONE;end if;--Indicate the transaction has completedwhen DONE =>write_state <= IDLE;when others =>write_state <= START;end case;end if;end process;process(clk, reset)beginif (reset = '0') thenread_state <= IDLE;axi_araddr <= (others =>'0');axi_arprot <= (others =>'0');axi_arvalid <= '0';axi_rready <= '0';elsif rising_edge(clk) thencase read_state iswhen IDLE =>if start_read = '1' thenread_state <= START;end if;--Send read addresswhen 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 addresswhen CHECK_ADDR_RESP =>if (axi_arready = '1' ) thenaxi_araddr <= (others => '0');axi_arprot <= (others => '0');axi_arvalid <= '0';read_state <= READ_DATA;elseread_state <= CHECK_ADDR_RESP;end if;s_axil_valid <= '0';--Read data from the slavewhen READ_DATA =>s_axil_data <= axi_rdata;if (axi_rvalid = '1') thens_axil_valid <= '1';read_state <= DONE;elses_axil_valid <= '0';read_state <= READ_DATA;end if;axi_rready <= '1';--Indicate the transaction has completedwhen 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 Package
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 resetclk_freq : natural := 100_000_000; -- oscillator frequencybaud_rate : natural := 115200 -- baud rate);port (--!System Inputsclk : in std_logic;reset : in std_logic;--!External Interfacesrx : in std_logic;tx : out std_logic;--! Master AXIS Interfacem_axis_tready : in std_logic;m_axis_tdata : out std_logic_vector(7 downto 0);m_axis_tvalid : out std_logic;--! Slave AXIS Interfaces_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 isconstant 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 signalssignal baud_en : std_logic := '0';signal meta_reg : std_logic_vector(3 downto 0) := (others => '0'); -- fe detection toosignal capture : std_logic_vector(7 downto 0) := (others => '0'); -- data and paritysignal 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');beginprocess (reset, clk)beginif reset = reset_level thencurrent_state <= idle;payload <= (others => '0');load_tx <= '0';elsif rising_edge(clk) thenload_tx <= '0';case current_state iswhen idle =>if s_axis_tvalid = '1' thencurrent_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' thencurrent_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 TXbeginif reset = reset_level thenbaud_counter <= (others => '0');baud_en <= '0';elsif rising_edge(clk) thenbaud_en <= '0';if (load_tx = '1') thenbaud_counter <= (others => '0');elsif (baud_counter = bit_period) thenbaud_en <= '1';baud_counter <= (others => '0');elsebaud_counter <= baud_counter + 1;end if;end if;end process;process (reset, clk)--!metastability protection rx signalbeginif reset = reset_level thenmeta_reg <= (others => '1');elsif rising_edge(clk) thenmeta_reg <= meta_reg(meta_reg'high - 1 downto meta_reg'low) & rx;end if;end process;process (reset, clk)beginif reset = reset_level thenpos_count <= 0;bit_count <= 0;capture <= (others => '0');rx_state <= idle;m_axis_tvalid <= '0';m_axis_tdata <= (others => '0');elsif rising_edge(clk) thencase rx_state iswhen idle =>m_axis_tvalid <= '0';if meta_reg(meta_reg'high downto meta_reg'high - 1) = fe_det thenpos_count <= 0;bit_count <= 0;capture <= (others => '0');rx_state <= start;end if;when start =>if bit_count = bit_period thenbit_count <= 0;rx_state <= sample;elsebit_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) thencapture <= meta_reg(meta_reg'high) & capture(capture'high downto capture'low + 1);elsif bit_count = bit_period thenif pos_count = 8 thenrx_state <= check;elsepos_count <= pos_count + 1;bit_count <= 0;end if;end if;when check =>if parity(capture) = '1' thenm_axis_tvalid <= '1';m_axis_tdata <= capture(7 downto 0);rx_state <= wait_axis;elsem_axis_tvalid <= '1';m_axis_tdata <= capture(7 downto 0);rx_state <= wait_axis;end if;when wait_axis =>if m_axis_tready = '1' thenm_axis_tvalid <= '0';rx_state <= idle;end if;end case;end if;end process;op_uart : process (reset, clk)beginif reset = reset_level thentx_reg <= (others => '1');tmr_reg <= (others => '0');elsif rising_edge(clk) thenif load_tx = '1' thentx_reg <= stop_bit & not(parity(payload)) & payload & start_bit ;tmr_reg <= (others => '1');elsif baud_en = '1' thentx_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 isfunction 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 isfunction vector_size(clk_freq, baud_rate : real) return integer isvariable div : real;variable res : real;begindiv := (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 isvariable y : std_logic := '0';beginfor i in a'range loopy := y xor a(i);end loop;return y;end parity;end package body adiuvo_uart;Top Level
LIBRARY ieee;USE ieee.std_logic_1164.all;USE ieee.numeric_std.all;entity top_level isport(clk : in std_logic;reset : in std_logic;rx : in std_logic;tx : out std_logic);-- Declarationsend 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 declarationssignal 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 Declarationscomponent axi_protocolgeneric (G_AXIL_DATA_WIDTH : integer := 32; --Width of AXI Lite data busG_AXI_ADDR_WIDTH : integer := 32; --Width of AXI Lite Address BuG_AXI_ID_WIDTH : integer := 8; --Width of AXI ID BusG_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_wrapperport (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 uartgeneric (reset_level : std_logic := '0'; -- reset level which causes a resetclk_freq : natural := 100_000_000; -- oscillator frequencybaud_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_offfor 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_onbegin-- Instance port mappings.U_0 : axi_protocolgeneric map (G_AXIL_DATA_WIDTH => 32, --Width of AXI Lite data busG_AXI_ADDR_WIDTH => 32, --Width of AXI Lite Address BuG_AXI_ID_WIDTH => 1, --Width of AXI ID BusG_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_wrapperport 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 : uartgeneric map (reset_level => '0', -- reset level which causes a resetclk_freq => 100_000_000, -- oscillator frequencybaud_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;Creating the XDCs The only difference between the three implementations that I am going to do is in the constraint files. We will need to create one constraint for each of the target boards.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 standardset_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 standardset_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 standardset_property IOSTANDARD LVCMOS33 [get_ports reset]set_property IOSTANDARD LVCMOS33 [get_ports rx]set_property IOSTANDARD LVCMOS33 [get_ports tx]Creating the FuseSoC Core With the RTL and script created the next step is to create the.core file and.conf file.The first thing to do is to create the.core file this will be split into several sections, the first bit is to define the CAPI version and the core library, providing its name and description
CAPI=2:name: adiuvo::hackster:0.1description: Implementation for Hackster ProjectThe next step is to create the filesets, these are split into several different groups. The first of these I have named the core group and these are the files which are common across all of the implementations.For each of these files we also define the library and the file type which for this example is vhdl.The next step is to define the tcl scripts which create the IP Integrator design. As there is no difference in configuration between the three target boards. This file is also common across all implementations.If we were creating a Zynq or Zynq MPSoC design which requires a specific board configuration, we would need variants of the file for each board which defined the PS configuration.The next file sets are the IO constraints, there is one file set for each of the target boards required.
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}The final step is to define the targets, here I define a default target which contains the core fileset. I then define three more targets, one for each of the target boards. For each target we define the tool to be AMD Vivado™ Design Suite, and append the filesets required for that particular target.In this case it is the IO fileset and the tcl scripts, to demonstrate how you would use the TCL script if each target was different.For each target we also need to define the top level module and of course the target device in 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-iWith the.core file completed we can double check that we are able to see the library we have just defined.The next stage is to define the fusesoc.conf file defining the location of the cores
[library.hackster]location = C:/hdl_projects/hackster_fusesocsync-uri = C:/hdl_projects/hackster_fusesoc/sync-type = localauto-sync = falseThis is very simple, as all we have in this example is one core library called Hackster.We can check this using the command
fusesoc core listThis should provide a output similar to the below FuseSoC Results With the source code created we can build the three examples by running he commandsAMD Artix™ 7
fusesoc --verbose run --target=artix7 --no-export hacksterAMD Kintex™ UltraSacle™
fusesoc --verbose run --target=kintexus--no-export hacksterAMD Spartan™ 7
fusesoc --verbose run --target=spartan7--no-export hacksterYou will see the transcript of the implementation scroll past as the project builds. Once completed you will, see a message showing the bitstream generation has been completed. We can run these commands and will see a build directory Looking under the build directory we will see a directory named after the core and under that a directory for each target and associated AMD Vivado™ Design Suite Project. Examining one of the projects you will see the project and all of the scripts created by FuseSoC. You will be able to find the bit file under the <project name>/runs directory Wrap Up This project has outlined how we are able to script our FPGA implementations using FuseSoC which enables us to easily and simply target a new FPGA device. As FuseSoC is a package manager and build system it allows us to work with our libraries of IP components while also providing the ability to with IP Integrator to leverage IP from AMD Vivado™ Design Suite.While I have not covered it in this project we are able to use FuseSoC for verification, due to its support for a range of simulators.