0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

FPGA技术江湖 来源:OpenFPGA 2024-11-20 16:12 次阅读

以下文章来源于 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
cec2aeca-a6d0-11ef-93f3-92fbcf53809c.png

下一步是安装 FuseSoC

pip3install--upgradefusesoc
cecde056-a6d0-11ef-93f3-92fbcf53809c.png

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

fusesoc--version

可以看到类似下面的内容

cedea008-a6d0-11ef-93f3-92fbcf53809c.png

FuseSoC 结构

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

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

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

cee87cc2-a6d0-11ef-93f3-92fbcf53809c.png

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

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

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

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

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

cf0e4826-a6d0-11ef-93f3-92fbcf53809c.png

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

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

fusesoclibraryadd/path/to/directory
cf198c4a-a6d0-11ef-93f3-92fbcf53809c.png

使用 FuseSoC

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

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

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

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

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

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

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

#StartanewprojectoropenanexistingoneinVivado
#OpentheIPIntegratordesigntool
create_bd_design"design_1"

#AddanAXIBRAMController
setaxi_bram_ctrl[create_bd_cell-typeip-vlnvxilinx.comaxi_bram_ctrl:4.1axi_bram_ctrl_0]

#ConfiguretheAXIBRAMControllerforAXI4-Liteinterface
set_propertyCONFIG.PROTOCOL{AXI4LITE}[get_bd_cells$axi_bram_ctrl]

#AddaBlockRAM(BRAM)
setbram[create_bd_cell-typeip-vlnvxilinx.comblk_mem_gen:8.4bram_0]

#ConnecttheBRAMControllertotheBRAM
connect_bd_intf_net-intf_netS_AXI$axi_bram_ctrl/BRAM_PORTA$bram/BRAM_PORTA

#MakeAXIinterface,clock,andresetexternal
#ExposetheAXIinterfacetoexternalports
make_bd_intf_pins_external[get_bd_intf_pins$axi_bram_ctrl/S_AXI]

#Exposetheclocktoanexternalport
make_bd_pins_external[get_bd_pins$axi_bram_ctrl/s_axi_aclk]

#Exposetheresettoanexternalport
make_bd_pins_external[get_bd_pins$axi_bram_ctrl/s_axi_aresetn]

#Assignaddresses
assign_bd_address

#Saveandvalidatethedesign
validate_bd_design
save_bd_design

#GeneratetheHDLwrapperforthedesignandcapturethegeneratedfilename
setwrapper_file[make_wrapper-files[get_filesdesign_1.bd]-top]

#Addthegeneratedwrapperfiletotheproject
add_files$wrapper_file

#Updatetheprojecthierarchytoincludethenewwrapperfile
update_compile_order-filesetsources_1

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

cf204d78-a6d0-11ef-93f3-92fbcf53809c.png

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

协议文件

libraryieee;
useieee.std_logic_1164.all;
useieee.numeric_std.all;
--Declareentity
entityaxi_protocolis
generic(
G_AXIL_DATA_WIDTH:integer:=32;--WidthofAXILitedatabus
G_AXI_ADDR_WIDTH:integer:=32;--WidthofAXILiteAddressBu
G_AXI_ID_WIDTH:integer:=8;--WidthofAXIIDBus
G_AXI_AWUSER_WIDTH:integer:=1--WidthofAXIAWUserbus
);
port(
--Masterclock&reset
clk:instd_ulogic;--Systemclock
reset:instd_ulogic;--Systemreset,asyncactivelow
--!MasterAXISInterface
m_axis_tready:instd_logic;
m_axis_tdata:outstd_logic_vector(7downto0);
m_axis_tvalid:outstd_logic;
--!SlaveAXISInterface
s_axis_tready:outstd_logic;
s_axis_tdata:instd_logic_vector(7downto0);
s_axis_tvalid:instd_logic;
--!AXILInterface
--!Writeaddress
axi_awaddr:outstd_logic_vector(G_AXI_ADDR_WIDTH-1downto0);
axi_awprot:outstd_logic_vector(2downto0);
axi_awvalid:outstd_logic;
--!writedata
axi_wdata:outstd_logic_vector(G_AXIL_DATA_WIDTH-1downto0);
axi_wstrb:outstd_logic_vector(G_AXIL_DATA_WIDTH/8-1downto0);
axi_wvalid:outstd_logic;
--!writeresponse
axi_bready:outstd_logic;
--!readaddress
axi_araddr:outstd_logic_vector(G_AXI_ADDR_WIDTH-1downto0);
axi_arprot:outstd_logic_vector(2downto0);
axi_arvalid:outstd_logic;
--!readdata
axi_rready:outstd_logic;
--writeaddress
axi_awready:instd_logic;
--writedata
axi_wready:instd_logic;
--writeresponse
axi_bresp:instd_logic_vector(1downto0);
axi_bvalid:instd_logic;
--readaddress
axi_arready:instd_logic;
--readdata
axi_rdata:instd_logic_vector(G_AXIL_DATA_WIDTH-1downto0);
axi_rresp:instd_logic_vector(1downto0);
axi_rvalid:instd_logic
);
endentityaxi_protocol;
architecturertlofaxi_protocolis
constantC_SINGLE_READ:std_logic_vector(7downto0):=x"05";
constantC_SINGLE_WRITE:std_logic_vector(7downto0):=x"09";
constantC_NUMB_ADDR_BYTES:integer:=4;
constantC_NUMB_LENGTH_BYTES:integer:=1;
constantC_NUMB_DATA_BYTES:integer:=4;
constantC_NUMB_AXIL_DATA_BYTES:integer:=4;
constantC_NUMB_CRC_BYTES:integer:=4;
constantC_MAX_NUMB_BYTES:integer:=4;--maxnumberoftheaboveconstantfornumberofbytes
constantC_ZERO_PAD:std_logic_vector(7downto0):=(others=>'0');
typet_fsmis(idle,address,length,dummy,write_payload,read_payload,crc,write_axil,write_axi,read_axi,read_axil);
typet_op_fsmis(idle,output,check);
typet_arrayisarray(0to7)ofstd_logic_vector(31downto0);
typeaxil_read_fsmis(IDLE,START,CHECK_ADDR_RESP,READ_DATA,DONE);
typeaxil_write_fsmis(IDLE,START,CHECK_ADDR_RESP,WRITE_DATA,RESP_READY,CHECK_RESP,DONE);
signalwrite_state:axil_write_fsm;
signalread_state:axil_read_fsm;
signals_current_state:t_fsm;
signals_command:std_logic_vector(7downto0);
signals_address:std_logic_vector((C_NUMB_ADDR_BYTES*8)-1downto0);
signals_length:std_logic_vector(7downto0);
signals_length_axi:std_logic_vector(7downto0);
signals_buf_cnt:unsigned(7downto0);
signals_byte_pos:integerrange0toC_MAX_NUMB_BYTES;
signals_num_bytes:integerrange0toC_MAX_NUMB_BYTES;
signals_s_tready:std_logic;
signals_write_buffer:t_array:=(others=>(others=>'0'));
signals_read_buffer:t_array:=(others=>(others=>'0'));
signals_write_buffer_temp:std_logic_vector(31downto0);
signals_read_buffer_temp:std_logic_vector(31downto0);
--axillitedatainterface
signals_axil_data:std_logic_vector(G_AXIL_DATA_WIDTH-1downto0);
signals_axil_valid:std_logic;
signals_axil_idata:std_logic_vector(G_AXIL_DATA_WIDTH-1downto0);
--aximstream
signals_opptr:unsigned(7downto0);
signals_start:std_logic;
signals_op_state:t_op_fsm;
signals_op_byte:integerrange0toC_MAX_NUMB_BYTES;
signalstart_read:std_logic;
signalstart_write:std_logic;
signals_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 =>--todoneedstocheckthecommandisvalid
s_buf_cnt<= (others =>'0');
if(s_axis_tvalid='1'ands_s_tready='1')and
(s_axis_tdata=C_SINGLE_READors_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 =>
ifs_byte_pos=0then
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 =>
ifs_byte_pos=0then
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 =>
ifs_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 =>
ifs_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;
endcase;
endif;
endprocess;
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 =>
ifs_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');
casewrite_stateis
--Sendwriteaddress
whenIDLE=>
ifstart_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');
endif;
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 =>
ifstart_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 封装

libraryieee;
useieee.std_logic_1164.all;
useieee.numeric_std.all;
useieee.math_real.all;
usework.adiuvo_uart.all;
entityuartisgeneric(
reset_level:std_logic:='0';--resetlevelwhichcausesareset
clk_freq:natural:=100_000_000;--oscillatorfrequency
baud_rate:natural:=115200--baudrate
);
port(
--!SystemInputs
clk:instd_logic;
reset:instd_logic;
--!ExternalInterfaces
rx:instd_logic;
tx:outstd_logic;
--!MasterAXISInterface
m_axis_tready:instd_logic;
m_axis_tdata:outstd_logic_vector(7downto0);
m_axis_tvalid:outstd_logic;
--!SlaveAXISInterface
s_axis_tready:outstd_logic;
s_axis_tdata:instd_logic_vector(7downto0);
s_axis_tvalid:instd_logic
);
endentity;
architecturertlofuartis
constantbit_period:integer:=(clk_freq/baud_rate)-1;
typecntrl_fsmis(idle,set_tx,wait_tx);
typerx_fsmis(idle,start,sample,check,wait_axis);
signalcurrent_state:cntrl_fsm;--:=idle;
signalrx_state:rx_fsm;--:=idle;
signalbaud_counter:unsigned(vector_size(real(clk_freq),real(baud_rate))downto0):=(others=>'0');--timerforoutgoingsignals
signalbaud_en:std_logic:='0';
signalmeta_reg:std_logic_vector(3downto0):=(others=>'0');--fedetectiontoo
signalcapture:std_logic_vector(7downto0):=(others=>'0');--dataandparity
signalbit_count:integerrange0to1023:=0;
signalpos_count:integerrange0to15:=0;
signalrunning:std_logic:='0';
signalload_tx:std_logic:='0';
signalcomplete:std_logic:='0';
signaltx_reg:std_logic_vector(11downto0):=(others=>'0');
signaltmr_reg:std_logic_vector(11downto0):=(others=>'0');
signalpayload:std_logic_vector(7downto0):=(others=>'0');
constantzero:std_logic_vector(tmr_reg'range):=(others=>'0');
begin
process(reset,clk)
begin
ifreset=reset_levelthen
current_state<= idle;
payload       <= (others =>'0');
load_tx<= '0';
elsif rising_edge(clk) then
load_tx <= '0';
case current_state is
when idle =>
ifs_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 =>
ifcomplete='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');
elsifrising_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');
elsifrising_edge(clk)then
caserx_stateis
whenidle=>
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 =>
ifbit_count=bit_periodthen
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 =>
ifparity(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 =>
ifm_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');
elsifrising_edge(clk)then
ifload_tx='1'then
tx_reg<= stop_bit & not(parity(payload)) & payload & start_bit ;
tmr_reg <= (others =>'1');
elsifbaud_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模块

LIBRARYieee;
USEieee.std_logic_1164.all;
USEieee.numeric_std.all;
entitytop_levelis
port(
clk:instd_logic;
reset:instd_logic;
rx:instd_logic;
tx:outstd_logic
);
--Declarations
endentitytop_level;

LIBRARYieee;
USEieee.std_logic_1164.all;
USEieee.numeric_std.all;
libraryUNISIM;
useUNISIM.VCOMPONENTS.ALL;
useieee.math_real.all;
architecturestructoftop_levelis
--Architecturedeclarations
--Internalsignaldeclarations
signalS_AXI_0_arready:STD_LOGIC;
signalS_AXI_0_awready:STD_LOGIC;
signalS_AXI_0_bresp:STD_LOGIC_VECTOR(1downto0);
signalS_AXI_0_bvalid:STD_LOGIC;
signalS_AXI_0_rdata:STD_LOGIC_VECTOR(31downto0);
signalS_AXI_0_rresp:STD_LOGIC_VECTOR(1downto0);
signalS_AXI_0_wready:STD_LOGIC;
signalS_AXI_0_wvalid:STD_LOGIC;
signalaxi_araddr:std_logic_vector(31downto0);
signalaxi_arprot:std_logic_vector(2downto0);
signalaxi_arvalid:std_logic;
signalaxi_awaddr:std_logic_vector(31downto0);
signalaxi_awprot:std_logic_vector(2downto0);
signalaxi_awvalid:std_logic;
signalaxi_bready:std_logic;
signalaxi_rready:std_logic;
signalaxi_rvalid:std_logic;
signalaxi_wdata:std_logic_vector(31downto0);
signalaxi_wstrb:std_logic_vector(3downto0);
signalm_axis_tdata:std_logic_vector(7downto0);
signalm_axis_tready:std_logic;
signalm_axis_tvalid:std_logic;
signals_axis_tdata:std_logic_vector(7downto0);
signals_axis_tready:std_logic;
signals_axis_tvalid:std_logic;
--ComponentDeclarations
componentaxi_protocol
generic(
G_AXIL_DATA_WIDTH:integer:=32;--WidthofAXILitedatabus
G_AXI_ADDR_WIDTH:integer:=32;--WidthofAXILiteAddressBu
G_AXI_ID_WIDTH:integer:=8;--WidthofAXIIDBus
G_AXI_AWUSER_WIDTH:integer:=1--WidthofAXIAWUserbus
);
port(
axi_arready:instd_logic;
axi_awready:instd_logic;
axi_bresp:instd_logic_vector(1downto0);
axi_bvalid:instd_logic;
axi_rdata:instd_logic_vector(31downto0);
axi_rresp:instd_logic_vector(1downto0);
axi_rvalid:instd_logic;
axi_wready:instd_logic;
clk:instd_ulogic;
m_axis_tready:instd_logic;
reset:instd_ulogic;
s_axis_tdata:instd_logic_vector(7downto0);
s_axis_tvalid:instd_logic;
axi_araddr:outstd_logic_vector(31downto0);
axi_arprot:outstd_logic_vector(2downto0);
axi_arvalid:outstd_logic;
axi_awaddr:outstd_logic_vector(31downto0);
axi_awprot:outstd_logic_vector(2downto0);
axi_awvalid:outstd_logic;
axi_bready:outstd_logic;
axi_rready:outstd_logic;
axi_wdata:outstd_logic_vector(31downto0);
axi_wstrb:outstd_logic_vector(3downto0);
axi_wvalid:outstd_logic;
m_axis_tdata:outstd_logic_vector(7downto0);
m_axis_tvalid:outstd_logic;
s_axis_tready:outstd_logic
);
endcomponentaxi_protocol;
componentdesign_1_wrapper
port(
S_AXI_0_araddr:inSTD_LOGIC_VECTOR(11downto0);
S_AXI_0_arprot:inSTD_LOGIC_VECTOR(2downto0);
S_AXI_0_arvalid:inSTD_LOGIC;
S_AXI_0_awaddr:inSTD_LOGIC_VECTOR(11downto0);
S_AXI_0_awprot:inSTD_LOGIC_VECTOR(2downto0);
S_AXI_0_awvalid:inSTD_LOGIC;
S_AXI_0_bready:inSTD_LOGIC;
S_AXI_0_rready:inSTD_LOGIC;
S_AXI_0_wdata:inSTD_LOGIC_VECTOR(31downto0);
S_AXI_0_wstrb:inSTD_LOGIC_VECTOR(3downto0);
S_AXI_0_wvalid:inSTD_LOGIC;
s_axi_aclk_0:inSTD_LOGIC;
s_axi_aresetn_0:inSTD_LOGIC;
S_AXI_0_arready:outSTD_LOGIC;
S_AXI_0_awready:outSTD_LOGIC;
S_AXI_0_bresp:outSTD_LOGIC_VECTOR(1downto0);
S_AXI_0_bvalid:outSTD_LOGIC;
S_AXI_0_rdata:outSTD_LOGIC_VECTOR(31downto0);
S_AXI_0_rresp:outSTD_LOGIC_VECTOR(1downto0);
S_AXI_0_rvalid:outSTD_LOGIC;
S_AXI_0_wready:outSTD_LOGIC
);
endcomponentdesign_1_wrapper;
componentuart
generic(
reset_level:std_logic:='0';--resetlevelwhichcausesareset
clk_freq:natural:=100_000_000;--oscillatorfrequency
baud_rate:natural:=115200--baudrate
);
port(
clk:instd_logic;
m_axis_tready:instd_logic;
reset:instd_logic;
rx:instd_logic;
s_axis_tdata:instd_logic_vector(7downto0);
s_axis_tvalid:instd_logic;
m_axis_tdata:outstd_logic_vector(7downto0);
m_axis_tvalid:outstd_logic;
s_axis_tready:outstd_logic;
tx:outstd_logic
);
endcomponentuart;
--Optionalembeddedconfigurations
--pragmasynthesis_off
forall:axi_protocoluseentitysrc.axi_protocol;
forall:design_1_wrapperuseentitysrc.design_1_wrapper;
forall:uartuseentitysrc.uart;
--pragmasynthesis_on
begin
--Instanceportmappings.
U_0:axi_protocol
genericmap(
G_AXIL_DATA_WIDTH=>32,--WidthofAXILitedatabus
G_AXI_ADDR_WIDTH=>32,--WidthofAXILiteAddressBu
G_AXI_ID_WIDTH=>1,--WidthofAXIIDBus
G_AXI_AWUSER_WIDTH=>1--WidthofAXIAWUserbus
)
portmap(
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
portmap(
S_AXI_0_araddr=>axi_araddr(11downto0),
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(11downto0),
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
genericmap(
reset_level=>'0',--resetlevelwhichcausesareset
clk_freq=>100_000_000,--oscillatorfrequency
baud_rate=>115200--baudrate
)
portmap(
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
);
endarchitecturestruct;

创建 XDC

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

AMD Spartan 7

set_propertyPACKAGE_PINR2[get_portsclk]
set_propertyIOSTANDARDLVCMOS33[get_portsclk]
create_clock-period10.000-namesys_clk[get_portsclk]
set_propertyPACKAGE_PINL17[get_portsreset]
set_propertyPACKAGE_PINL18[get_portsrx]
set_propertyPACKAGE_PINM14[get_portstx]
#setI/Ostandard
set_propertyIOSTANDARDLVCMOS33[get_portsreset]
set_propertyIOSTANDARDLVCMOS33[get_portsrx]
set_propertyIOSTANDARDLVCMOS33[get_portstx]

AMD Artix 7

set_propertyPACKAGE_PINE3[get_portsclk]
set_propertyIOSTANDARDLVCMOS33[get_portsclk]
create_clock-period10.000-namesys_clk[get_portsclk]
set_propertyPACKAGE_PING13[get_portsreset]
set_propertyPACKAGE_PINB11[get_portsrx]
set_propertyPACKAGE_PINA11[get_portstx]
#setI/Ostandard
set_propertyIOSTANDARDLVCMOS33[get_portsreset]
set_propertyIOSTANDARDLVCMOS33[get_portsrx]
set_propertyIOSTANDARDLVCMOS33[get_portstx]

AMD Kintex UltraSacle

set_propertyPACKAGE_PINAF9[get_portsclk]
set_propertyIOSTANDARDLVCMOS33[get_portsclk]
create_clock-period10.000-namesys_clk[get_portsclk]
set_propertyPACKAGE_PINAE8[get_portsreset]
set_propertyPACKAGE_PINAE10[get_portsrx]
set_propertyPACKAGE_PINAD10[get_portstx]
#setI/Ostandard
set_propertyIOSTANDARDLVCMOS33[get_portsreset]
set_propertyIOSTANDARDLVCMOS33[get_portsrx]
set_propertyIOSTANDARDLVCMOS33[get_portstx]

创建 FuseSoC 核心

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

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

CAPI=2:

name:adiuvo:0.1
description:ImplementationforHacksterProject

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

对于每个文件,我们还定义了库和文件类型,在本例中为 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 的核心库。

我们可以使用命令来检查

fusesoccorelist

可以得到以下输出

cf361180-a6d0-11ef-93f3-92fbcf53809c.png

FuseSoC 结果

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

AMD Artix 7

fusesoc--verboserun--target=artix7--no-exporthackster

AMD Kintex UltraSacle

fusesoc--verboserun--target=kintexus--no-exporthackster

AMD Spartan 7

fusesoc--verboserun--target=spartan7--no-exporthackster

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

cf454330-a6d0-11ef-93f3-92fbcf53809c.jpg

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

cf5db1d6-a6d0-11ef-93f3-92fbcf53809c.png

总结

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

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1628

    文章

    21728

    浏览量

    602944
  • amd
    amd
    +关注

    关注

    25

    文章

    5466

    浏览量

    134077
  • Xilinx
    +关注

    关注

    71

    文章

    2166

    浏览量

    121284
  • 移植
    +关注

    关注

    1

    文章

    379

    浏览量

    28124

原文标题:多平台FPGA工程快速移植与构建

文章出处:【微信号:HXSLH1010101010,微信公众号:FPGA技术江湖】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    LabVIEW实时与FPGA助您快速构建工业高确定性应用

    LabVIEW实时与FPGA助您快速构建工业高确定性应用议程?工业高确定性应用的解决方案–实时系统与FPGA?NI提供全面的解决方案–快速
    发表于 10-28 10:27

    善用Vivado工程配置文件xpr快速工程创建

    根据需要定制自己的移植配置文件,这对于要多次创建基于同一个FPGA器件平台工程而言,非常高效。这其实也是脚本开发相对于GUI开发方式的一个主要优势。
    发表于 10-19 18:05

    如何利用FPGA构建PCI Express端点器件最佳平台

    如何利用FPGA构建PCI Express端点器件最佳平台
    发表于 04-29 06:54

    快速移植OpenHarmony到三方芯片平台的方法

    移植概述本文面向希望将OpenHarmony移植到三方芯片平台硬件的开发者,介绍一种借助三方芯片平台自带Linux内核的现有能力,快速
    发表于 04-12 11:08

    赛灵思平台Virtex-4 FPGA的性能及应用

    赛灵思平台Virtex-4 FPGA的性能及应用 赛灵思(Xilinx)的Virtex-4现场可编程门阵列(FPGA)是首款基于ASMBL(Advanced Silicon Mod
    发表于 06-26 08:11 41次下载

    基于FPGA的NoC验证平台构建

    针对基于软件仿真片上网络NoC(Network on Chip)效率低的问题,提出基于FPGA的NoC验证平台构建方案。该平台集成可重用的流量产生器TG(Traffic Generat
    发表于 01-04 16:24 12次下载

    基于FPGA模无线基站

    FPGA 类高性能可编程逻辑器件,正是模无线基站的最佳构建平台之一
    发表于 09-28 10:24 853次阅读

    移植Linux到晶心平台

    鉴于越来越多使用者将Linux移植到晶心平台(Andes Embedded)上(AndesCore N12或N10),本文的目的在协助使用者快速、有效率的将Linux 移植到自建的
    发表于 04-11 10:10 933次阅读
    <b class='flag-5'>移植</b>Linux到晶心<b class='flag-5'>平台</b>

    FPGA为基础的模无线基站

    FPGA 类高性能可编程逻辑器件,正是模无线基站的最佳构建平台之一。Xilinx率先发布和量产的65nm平台FPGA,则以大量先进技术和全
    发表于 07-31 09:44 1158次阅读

    ucos_ii 在microblaze平台上的移植

    Xilinx FPGA工程例子源码:ucos_ii 在microblaze平台上的移植
    发表于 06-07 14:41 12次下载

    如何快速构建一个移动跨平台视频通话应用

    今天我们就来看一下如何使用 Agora Flutter SDK 快速构建一个简单的移动跨平台视频通话应用。
    的头像 发表于 02-24 06:01 2825次阅读
    如何<b class='flag-5'>快速</b><b class='flag-5'>构建</b>一个移动跨<b class='flag-5'>平台</b>视频通话应用

    uCOSIII系统移植(二)构建多任务

    uCOSIII构建多任务LED.CF407时钟挂载LED.H(宏定义狂魔)APP.C完整工程下载上一节移植构建了模板和创建了单任务,这一节来构建
    发表于 11-30 15:51 1次下载
    uCOSIII系统<b class='flag-5'>移植</b>(二)<b class='flag-5'>构建</b>多任务

    FPGA知识汇集-ASIC向FPGA移植

    将ASIC设计移植FPGA芯片中,对于大部分设计团队来讲都是巨大的挑战。主要体现在:ASIC的设计一般都非常大,往往需要做FPGA芯片划分;需要支持足够的处理性能;需要保证其功能的
    的头像 发表于 04-14 15:01 2139次阅读

    如何移植FPGA的例程

    在完成EDA作业后,抽空分享一下如何移植FPGA的例程。我EDA作业用的板子型号是Zybo-Z7,然后移植的是原子哥的HDMI实现方块移动例程。
    的头像 发表于 09-05 15:12 1899次阅读

    如何将这些SoC的逻辑功能原型正确的移植FPGA中?

    当SoC的规模在一片FPGA中装不下的时候,我们通常选择FPGA原型验证的平台来承载整个SoC系统。
    发表于 05-10 10:15 330次阅读