資料介紹
描述
這個項目是我完整詳細介紹帶有 ADC 和 DAC ZMOD 的 Eclypse Z7 的延續。正如我在那個項目中提到的那樣,我通過將 DDS Complier IP 模塊集成到模塊設計中并使用它來生成數字 1 MHz 正弦波數據以供 DAC ZMOD 輸出到其通道之一,從而添加到設計中。為了驗證數據,它將在 ADC ZMOD 的一個通道上讀取。我還將把它連接到我的信號分析儀上,看看物理 1MHz 波是什么樣子的。
首先,我將 DAC ZMOD 的第一個通道連接到 ADC ZMOD 的第一個通道:
作為參考的旁注,我再次使用 Vivado 和 Vitis 2019.2 版,并且我使用的項目與我在上一篇關于Eclypse Z7 的項目帖子中詳細介紹了如何創建的項目完全相同。
從 Vivado 中現有的硬件設計開始,首先需要修改的是塊設計。我非常喜歡使用 DDS 編譯器生成正弦波,因為它們是結構資源利用率和輸出波精度之間的最佳折衷。
打開模塊設計并將 DDS 編譯器添加到 IP 模塊設計,雙擊它以打開其定制窗口。
使用 DDS 編譯器 IP 的最大優勢之一是在更改輸出信號的頻率/相位時平滑/無縫過渡(因此您不必擔心相位不連續)。這就是為什么我喜歡使用流選項來實現輸出頻率/相位的可編程性。
我現在選擇只關注更改輸出頻率(通過相位增量可編程性)并將相位偏移可編程性設置為無:
對于帶有 DMA 的 AXI 流協議與 DAC ZMOD 接口以將正弦波寫出,流需要被打包,輸出tready,并在正弦波的每個周期結束時斷言 tlast。在 Data has TLAST 下選擇“Packet Framing”選項,并選中“Output TREADY”復選框。
DDS 編譯器會將其輸出寫入 Eclypse 的 DDR 內存,供 DAC 通過 DMA 引擎讀取。為了使集成盡可能簡單,我只是為 DDS 的輸出啟用了 DMA 引擎的寫入通道。由于數據一次傳輸一個周期的正弦波,因此還需要為寫入通道啟用 DRE(數據重新對齊引擎)。選中寫入通道的“允許未對齊傳輸”框(僅適用于寫入通道,因為裸機 Zmod 庫負責對齊數據以在讀取通道上傳輸)。
手動將 DDS 編譯器的 M_AXIS_DATA 輸出連接到 DAC 的 DMA 引擎的 S_AXIS_S2MM。DMA 的 S_AXIS_S2MM 端口的 tkeep 信號需要在其總線上保持高電平,以表示所有傳入數據都是有效的(包括任何零值數據字節)。為此,在設計中添加一個常量 IP 塊,將輸出寬度設置為與 DMA 的 S_AXIS_S2MM 端口的 tkeep 匹配,并將該值設置為總線上的高電平(在這種情況下,tkeep 信號為兩位寬,因此常數值將設置為 3,即 2'b11)。然后手動將其輸出連接到 DMA 的 S_AXIS_S2MM 端口。
我認為從 Zynq 芯片的可編程邏輯控制 DDS 編譯器的輸入會更容易。為了做到這一點,DDS 編譯器的相位輸入端口需要在框圖外部可用,方法是右鍵單擊端口名稱并選擇“Make external”選項。您將看到 DDS 編譯器輸入的 AXI 流總線端口出現。
來自 Zynq 處理系統的 FCLK_CLK1 時鐘也需要引出到模塊設計中的端口,以便在 Zynq 芯片的可編程邏輯中可供 HDL 使用。只需右鍵單擊 Zynq 處理系統 IP 塊上的 FCLK_CLK1 端口名稱并選擇“創建端口...”:
塊設計現在應該類似于以下內容:
后面要添加邏輯分析儀進行調試,標記DDS的相位輸入,連同DDS數據輸出,DAC的DMA的AXIS_MM2S和ADC的DMA的AXIS_S2MM進行調試。只需右鍵單擊每一行并選擇“調試”:
片刻之后,將出現連接自動化選項。選擇它并確保選中 AXI 協議檢查器選項的復選框。
連接自動化完成運行后,驗證模塊設計以檢查是否存在任何錯誤或嚴重警告,然后保存并關閉它。
由于已添加新的外部端口,因此需要為模塊設計創建新的 HDL 實例化。只需右鍵單擊 Sources Hierarchy 選項卡中的塊設計文件并選擇“Create HDL Wrapper...”選項即可完成此操作。它只會使用新的框圖實例化更新現有的框圖(就像在上一個項目中一樣,選擇讓 Vivado 自動更新和管理它的選項)。
然而,由于沒有一個新的外部端口將路由到 Zynq 芯片上的實際封裝引腳,并且還需要添加其他自定義 HDL,因此需要從頭開始創建一個新的頂級文件。即使有一個選項允許用戶在最后一步管理包裝器,我發現使用 Vivado 最好始終選擇自動管理選項,然后從頭開始創建自己的頂級文件并簡單地復制+粘貼來自自動生成的包裝器的框圖實例化。
對于這個設計,我正在創建我自己的三個設計源:新的自定義頂層文件、用于生成相位增量值以發送到 DDS 編譯器的邏輯,以及用于 AXI 流協議以與 DDS 編譯器通信的狀態機.
從 Flow Navigator 中選擇 Add Sources 選項,然后在彈出窗口中選擇 Add or create design sources。然后,該窗口將為您提供創建所需文件數量的選項(在本例中為三個 Verilog 模塊文件)。
我將頂層文件命名為“eclypse_top”,將相位增量邏輯文件命名為“bb_logic”,并將 AXI 流狀態機文件命名為“axis_sm”。
頂層文件主要是為塊設計實例化自動生成的 design_wrapper 文件的復制 + 粘貼。它還將實例化相位增量邏輯模塊。請注意,用于 DDS 編譯器和 FCLK_CLK1 的從 AXI 流信號從模塊端口說明符中被注釋掉,因為它們被重定向到相位增量邏輯模塊,而不是被路由到 Zynq 芯片上的封裝引腳。然后相位增量邏輯模塊負責例化 AXI 流狀態機模塊。
自定義頂級文件 Verilog:
module eclypse_top(
inout [14:0]DDR_addr,
inout [2:0]DDR_ba,
inout DDR_cas_n,
inout DDR_ck_n,
inout DDR_ck_p,
inout DDR_cke,
inout DDR_cs_n,
inout [3:0]DDR_dm,
inout [31:0]DDR_dq,
inout [3:0]DDR_dqs_n,
inout [3:0]DDR_dqs_p,
inout DDR_odt,
inout DDR_ras_n,
inout DDR_reset_n,
inout DDR_we_n,
// input [31:0]DDS_S_AXIS_PHASE_tdata,
// input DDS_S_AXIS_PHASE_tlast,
// output DDS_S_AXIS_PHASE_tready,
// input DDS_S_AXIS_PHASE_tvalid,
input DcoClk_0,
// output FCLK_CLK1,
inout FIXED_IO_ddr_vrn,
inout FIXED_IO_ddr_vrp,
inout [53:0]FIXED_IO_mio,
inout FIXED_IO_ps_clk,
inout FIXED_IO_ps_porb,
inout FIXED_IO_ps_srstb,
output adcClkIn_n_0,
output adcClkIn_p_0,
output adcSync_0,
input [1:0]btn_2bits_tri_i,
input [13:0]dADC_Data_0,
inout pmod_ja_pin10_io,
inout pmod_ja_pin1_io,
inout pmod_ja_pin2_io,
inout pmod_ja_pin3_io,
inout pmod_ja_pin4_io,
inout pmod_ja_pin7_io,
inout pmod_ja_pin8_io,
inout pmod_ja_pin9_io,
inout pmod_jb_pin10_io,
inout pmod_jb_pin1_io,
inout pmod_jb_pin2_io,
inout pmod_jb_pin3_io,
inout pmod_jb_pin4_io,
inout pmod_jb_pin7_io,
inout pmod_jb_pin8_io,
inout pmod_jb_pin9_io,
output [5:0]rgbled_6bits_tri_o,
output sADC_CS_0,
inout sADC_SDIO_0,
output sADC_Sclk_0,
output sCh1CouplingH_0,
output sCh1CouplingL_0,
output sCh1GainH_0,
output sCh1GainL_0,
output sCh2CouplingH_0,
output sCh2CouplingL_0,
output sCh2GainH_0,
output sCh2GainL_0,
output sDAC_CS_0,
output sDAC_ClkIO_0,
output sDAC_Clkin_0,
output [13:0]sDAC_Data_0,
output sDAC_EnOut_0,
output sDAC_Reset_0,
output sDAC_SCLK_0,
inout sDAC_SDIO_0,
output sDAC_SetFS1_0,
output sDAC_SetFS2_0,
output sRelayComH_0,
output sRelayComL_0,
input sys_clock
);
wire FCLK_CLK1;
wire [31:0]DDS_S_AXIS_PHASE_tdata;
wire DDS_S_AXIS_PHASE_tlast;
wire DDS_S_AXIS_PHASE_tready;
wire DDS_S_AXIS_PHASE_tvalid;
// block diagram instantiation
design_1 design_1_i(
.DDR_addr(DDR_addr),
.DDR_ba(DDR_ba),
.DDR_cas_n(DDR_cas_n),
.DDR_ck_n(DDR_ck_n),
.DDR_ck_p(DDR_ck_p),
.DDR_cke(DDR_cke),
.DDR_cs_n(DDR_cs_n),
.DDR_dm(DDR_dm),
.DDR_dq(DDR_dq),
.DDR_dqs_n(DDR_dqs_n),
.DDR_dqs_p(DDR_dqs_p),
.DDR_odt(DDR_odt),
.DDR_ras_n(DDR_ras_n),
.DDR_reset_n(DDR_reset_n),
.DDR_we_n(DDR_we_n),
.DDS_S_AXIS_PHASE_tdata(DDS_S_AXIS_PHASE_tdata),
.DDS_S_AXIS_PHASE_tlast(DDS_S_AXIS_PHASE_tlast),
.DDS_S_AXIS_PHASE_tready(DDS_S_AXIS_PHASE_tready),
.DDS_S_AXIS_PHASE_tvalid(DDS_S_AXIS_PHASE_tvalid),
.DcoClk_0(DcoClk_0),
.FCLK_CLK1(FCLK_CLK1),
.FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
.FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
.FIXED_IO_mio(FIXED_IO_mio),
.FIXED_IO_ps_clk(FIXED_IO_ps_clk),
.FIXED_IO_ps_porb(FIXED_IO_ps_porb),
.FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),
.adcClkIn_n_0(adcClkIn_n_0),
.adcClkIn_p_0(adcClkIn_p_0),
.adcSync_0(adcSync_0),
.btn_2bits_tri_i(btn_2bits_tri_i),
.dADC_Data_0(dADC_Data_0),
.pmod_ja_pin10_i(pmod_ja_pin10_i),
.pmod_ja_pin10_o(pmod_ja_pin10_o),
.pmod_ja_pin10_t(pmod_ja_pin10_t),
.pmod_ja_pin1_i(pmod_ja_pin1_i),
.pmod_ja_pin1_o(pmod_ja_pin1_o),
.pmod_ja_pin1_t(pmod_ja_pin1_t),
.pmod_ja_pin2_i(pmod_ja_pin2_i),
.pmod_ja_pin2_o(pmod_ja_pin2_o),
.pmod_ja_pin2_t(pmod_ja_pin2_t),
.pmod_ja_pin3_i(pmod_ja_pin3_i),
.pmod_ja_pin3_o(pmod_ja_pin3_o),
.pmod_ja_pin3_t(pmod_ja_pin3_t),
.pmod_ja_pin4_i(pmod_ja_pin4_i),
.pmod_ja_pin4_o(pmod_ja_pin4_o),
.pmod_ja_pin4_t(pmod_ja_pin4_t),
.pmod_ja_pin7_i(pmod_ja_pin7_i),
.pmod_ja_pin7_o(pmod_ja_pin7_o),
.pmod_ja_pin7_t(pmod_ja_pin7_t),
.pmod_ja_pin8_i(pmod_ja_pin8_i),
.pmod_ja_pin8_o(pmod_ja_pin8_o),
.pmod_ja_pin8_t(pmod_ja_pin8_t),
.pmod_ja_pin9_i(pmod_ja_pin9_i),
.pmod_ja_pin9_o(pmod_ja_pin9_o),
.pmod_ja_pin9_t(pmod_ja_pin9_t),
.pmod_jb_pin10_i(pmod_jb_pin10_i),
.pmod_jb_pin10_o(pmod_jb_pin10_o),
.pmod_jb_pin10_t(pmod_jb_pin10_t),
.pmod_jb_pin1_i(pmod_jb_pin1_i),
.pmod_jb_pin1_o(pmod_jb_pin1_o),
.pmod_jb_pin1_t(pmod_jb_pin1_t),
.pmod_jb_pin2_i(pmod_jb_pin2_i),
.pmod_jb_pin2_o(pmod_jb_pin2_o),
.pmod_jb_pin2_t(pmod_jb_pin2_t),
.pmod_jb_pin3_i(pmod_jb_pin3_i),
.pmod_jb_pin3_o(pmod_jb_pin3_o),
.pmod_jb_pin3_t(pmod_jb_pin3_t),
.pmod_jb_pin4_i(pmod_jb_pin4_i),
.pmod_jb_pin4_o(pmod_jb_pin4_o),
.pmod_jb_pin4_t(pmod_jb_pin4_t),
.pmod_jb_pin7_i(pmod_jb_pin7_i),
.pmod_jb_pin7_o(pmod_jb_pin7_o),
.pmod_jb_pin7_t(pmod_jb_pin7_t),
.pmod_jb_pin8_i(pmod_jb_pin8_i),
.pmod_jb_pin8_o(pmod_jb_pin8_o),
.pmod_jb_pin8_t(pmod_jb_pin8_t),
.pmod_jb_pin9_i(pmod_jb_pin9_i),
.pmod_jb_pin9_o(pmod_jb_pin9_o),
.pmod_jb_pin9_t(pmod_jb_pin9_t),
.rgbled_6bits_tri_o(rgbled_6bits_tri_o),
.sADC_CS_0(sADC_CS_0),
.sADC_SDIO_0(sADC_SDIO_0),
.sADC_Sclk_0(sADC_Sclk_0),
.sCh1CouplingH_0(sCh1CouplingH_0),
.sCh1CouplingL_0(sCh1CouplingL_0),
.sCh1GainH_0(sCh1GainH_0),
.sCh1GainL_0(sCh1GainL_0),
.sCh2CouplingH_0(sCh2CouplingH_0),
.sCh2CouplingL_0(sCh2CouplingL_0),
.sCh2GainH_0(sCh2GainH_0),
.sCh2GainL_0(sCh2GainL_0),
.sDAC_CS_0(sDAC_CS_0),
.sDAC_ClkIO_0(sDAC_ClkIO_0),
.sDAC_Clkin_0(sDAC_Clkin_0),
.sDAC_Data_0(sDAC_Data_0),
.sDAC_EnOut_0(sDAC_EnOut_0),
.sDAC_Reset_0(sDAC_Reset_0),
.sDAC_SCLK_0(sDAC_SCLK_0),
.sDAC_SDIO_0(sDAC_SDIO_0),
.sDAC_SetFS1_0(sDAC_SetFS1_0),
.sDAC_SetFS2_0(sDAC_SetFS2_0),
.sRelayComH_0(sRelayComH_0),
.sRelayComL_0(sRelayComL_0),
.sys_clock(sys_clock));
IOBUF pmod_ja_pin10_iobuf(
.I(pmod_ja_pin10_o),
.IO(pmod_ja_pin10_io),
.O(pmod_ja_pin10_i),
.T(pmod_ja_pin10_t));
IOBUF pmod_ja_pin1_iobuf(
.I(pmod_ja_pin1_o),
.IO(pmod_ja_pin1_io),
.O(pmod_ja_pin1_i),
.T(pmod_ja_pin1_t));
IOBUF pmod_ja_pin2_iobuf(
.I(pmod_ja_pin2_o),
.IO(pmod_ja_pin2_io),
.O(pmod_ja_pin2_i),
.T(pmod_ja_pin2_t));
IOBUF pmod_ja_pin3_iobuf(
.I(pmod_ja_pin3_o),
.IO(pmod_ja_pin3_io),
.O(pmod_ja_pin3_i),
.T(pmod_ja_pin3_t));
IOBUF pmod_ja_pin4_iobuf(
.I(pmod_ja_pin4_o),
.IO(pmod_ja_pin4_io),
.O(pmod_ja_pin4_i),
.T(pmod_ja_pin4_t));
IOBUF pmod_ja_pin7_iobuf(
.I(pmod_ja_pin7_o),
.IO(pmod_ja_pin7_io),
.O(pmod_ja_pin7_i),
.T(pmod_ja_pin7_t));
IOBUF pmod_ja_pin8_iobuf(
.I(pmod_ja_pin8_o),
.IO(pmod_ja_pin8_io),
.O(pmod_ja_pin8_i),
.T(pmod_ja_pin8_t));
IOBUF pmod_ja_pin9_iobuf(
.I(pmod_ja_pin9_o),
.IO(pmod_ja_pin9_io),
.O(pmod_ja_pin9_i),
.T(pmod_ja_pin9_t));
IOBUF pmod_jb_pin10_iobuf(
.I(pmod_jb_pin10_o),
.IO(pmod_jb_pin10_io),
.O(pmod_jb_pin10_i),
.T(pmod_jb_pin10_t));
IOBUF pmod_jb_pin1_iobuf(
.I(pmod_jb_pin1_o),
.IO(pmod_jb_pin1_io),
.O(pmod_jb_pin1_i),
.T(pmod_jb_pin1_t));
IOBUF pmod_jb_pin2_iobuf(
.I(pmod_jb_pin2_o),
.IO(pmod_jb_pin2_io),
.O(pmod_jb_pin2_i),
.T(pmod_jb_pin2_t));
IOBUF pmod_jb_pin3_iobuf(
.I(pmod_jb_pin3_o),
.IO(pmod_jb_pin3_io),
.O(pmod_jb_pin3_i),
.T(pmod_jb_pin3_t));
IOBUF pmod_jb_pin4_iobuf(
.I(pmod_jb_pin4_o),
.IO(pmod_jb_pin4_io),
.O(pmod_jb_pin4_i),
.T(pmod_jb_pin4_t));
IOBUF pmod_jb_pin7_iobuf(
.I(pmod_jb_pin7_o),
.IO(pmod_jb_pin7_io),
.O(pmod_jb_pin7_i),
.T(pmod_jb_pin7_t));
IOBUF pmod_jb_pin8_iobuf(
.I(pmod_jb_pin8_o),
.IO(pmod_jb_pin8_io),
.O(pmod_jb_pin8_i),
.T(pmod_jb_pin8_t));
IOBUF pmod_jb_pin9_iobuf(
.I(pmod_jb_pin9_o),
.IO(pmod_jb_pin9_io),
.O(pmod_jb_pin9_i),
.T(pmod_jb_pin9_t));
// phase increment logic module instantiation
bb_logic bb_logic_i(
.clk(FCLK_CLK1),
.DDS_S_AXIS_PHASE_tdata(DDS_S_AXIS_PHASE_tdata),
.DDS_S_AXIS_PHASE_tlast(DDS_S_AXIS_PHASE_tlast),
.DDS_S_AXIS_PHASE_tready(DDS_S_AXIS_PHASE_tready),
.DDS_S_AXIS_PHASE_tvalid(DDS_S_AXIS_PHASE_tvalid));
endmodule
對于 DDS 的輸出頻率,我暫時選擇對其進行硬核,以確保模塊設計中的 DDS 編譯器集成是正確的。由于 DAC 和 ADC 的最大采樣率為 100Ms/s,因此按照 Nyquist 的最大輸出頻率為 50MHz。我決定將 1MHz 作為一個簡單的數字(請參閱我最初的DDS 編譯器教程,了解我如何計算 1MHz 的十六進制值相位增量輸入)。
相位增量邏輯模塊文件 Verilog:
module bb_logic(
input clk,
output [31:0] DDS_S_AXIS_PHASE_tdata, // input to block design
output DDS_S_AXIS_PHASE_tlast, // input to block design
input DDS_S_AXIS_PHASE_tready, // output from block design
output DDS_S_AXIS_PHASE_tvalid // input to block design
);
wire [31:0] Freq;
wire [31:0] Freq_period;
// setting the phase increment value to static for now
assign Freq = 32'h28f5c2;
assign Freq_period = 32'd100; // 1000ns/10ns = 100 --> max value here is 16384
wire latch_tdata;
// AXI stream state machine instantiation
axis_sm axis_sm_i(
.clk(clk),
.reset(1'b1),
.start(1'b1),
.latch_tdata(latch_tdata),
.s_phase_tvalid(DDS_S_AXIS_PHASE_tvalid), // output
.s_phase_tlast(DDS_S_AXIS_PHASE_tlast), // output
.s_phase_tready(DDS_S_AXIS_PHASE_tready), // input
.s_phase_tdata(DDS_S_AXIS_PHASE_tdata), // output
.carrier_freq(Freq),
.carrier_period(Freq_period)
);
endmodule
AXI Stream 協議狀態機:
module axis_sm(
input clk,
input reset,
input start,
output reg latch_tdata,
output reg s_phase_tvalid,
output reg s_phase_tlast,
input s_phase_tready,
output reg [31:0] s_phase_tdata,
input [31:0] carrier_freq,
input [31:0] carrier_period
);
reg [4:0] state_reg;
reg [31:0] period_wait_cnt;
parameter init = 5'd0;
parameter WaitForStart = 5'd1;
parameter SetTvalidHigh = 5'd2;
parameter SetSlavePhaseValue = 5'd3;
parameter LatchTdata = 5'd4;
parameter CheckTready = 5'd5;
parameter WaitState = 5'd6;
parameter SetTlastHigh = 5'd7;
parameter WaitOneState = 5'd8;
parameter SetTlastLow = 5'd9;
parameter set_freq = 1'b0;
parameter set_phase = 1'b1;
parameter default_tdata = 32'h0;
always @ (posedge clk or posedge reset)
begin
// Default Outputs
latch_tdata <= 1'b0;
if (reset == 1'b0)
begin
s_phase_tdata[31:0] <= default_tdata;
state_reg <= init;
end
else
begin
case(state_reg)
init : //0
begin
latch_tdata <= 1'b0;
s_phase_tlast <= 1'b0;
s_phase_tvalid <= 1'b0;
period_wait_cnt <= 32'd0;
state_reg <= WaitForStart;
end
WaitForStart : //1
begin
if (start == 1'b1)
begin
state_reg <= SetTvalidHigh;
end
else
begin
state_reg <= WaitForStart;
end
end
SetTvalidHigh : //2
begin
s_phase_tvalid <= 1'b1;
state_reg <= SetSlavePhaseValue;
end
SetSlavePhaseValue : //3
begin
s_phase_tdata[31:0] <= carrier_freq;
state_reg <= LatchTdata;
end
LatchTdata : //4
begin
latch_tdata <= 1'b1;
state_reg <= CheckTready;
end
CheckTready : //5
begin
if (s_phase_tready == 1'b1)
begin
state_reg <= WaitState;
end
else if (start == 1'b0)
begin
state_reg <= init;
end
else
begin
state_reg <= CheckTready;
end
end
WaitState : //6
begin
if (period_wait_cnt >= carrier_period)
begin
period_wait_cnt <= 32'd0;
state_reg <= SetTlastHigh;
end
else
begin
period_wait_cnt <= period_wait_cnt + 1;
state_reg <= WaitState;
end
end
SetTlastHigh : //7
begin
s_phase_tlast <= 1'b1;
state_reg <= WaitOneState;
end
WaitOneState : //8
begin
state_reg <= SetTlastLow;
end
SetTlastLow : //9
begin
s_phase_tlast <= 1'b0;
state_reg <= WaitForStart;
end
endcase
end
end
endmodule
新的頂級文件與其他兩個文件一起完成后,保存所有文件,您將看到 Sources Hierarchy 自動更新。通過右鍵單擊 Sources Hierarchy 選項卡中的 eclypse_top 文件并選擇“設置為頂部”選項,將 eclypse_top 文件設置為項目的新頂部文件。
有了新的自定義頂級文件,暫時不再需要自動生成的文件。不過我發現如果以后再更新積木設計,還是有它就好了。再加上我多次了解到刪除 Vivado 自動生成的文件會導致工具出現未定義行為這一事實,我只是禁用了該文件。在 Sources Hierarchy 選項卡中右鍵單擊它,然后選擇“Disable”。更新后,您的 Sources Hierarchy 選項卡應類似于以下內容:
正如我在之前的項目中提到的那樣,基礎項目的時間安排已經結束。輸出到 SYZYGY 連接器上的 DAC Zmod 的數據線的保持時間不夠長。我在之前的項目中沒有修復它,因為我知道我在這個項目中的添加會在實現過程中改變設計的位置和布線,并最終在一定程度上改變時序。幸運的是,這個項目中的添加增加了足夠的延遲,我能夠簡單地延長約束文件中 DAC 數據線的保持時間(最后四行):
set_output_delay -clock [get_clocks sDAC_Clkin_0] -clock_fall -min -add_delay 0.330 [get_ports {sDAC_Data_0[*]}]
set_output_delay -clock [get_clocks sDAC_Clkin_0] -clock_fall -max -add_delay 0.250 [get_ports {sDAC_Data_0[*]}]
set_output_delay -clock [get_clocks sDAC_Clkin_0] -min -add_delay 0.330 [get_ports {sDAC_Data_0[*]}]
set_output_delay -clock [get_clocks sDAC_Clkin_0] -max -add_delay 0.150 [get_ports {sDAC_Data_0[*]}]
約束文件更新后,運行綜合、實現并生成比特流。完成后,驗證沒有錯誤或嚴重警告并導出硬件以在 Vitis 中使用。在“文件”菜單下,選擇“導出”下的“導出硬件...”選項。確認您正在導出到與 Vitis 工作區中現有硬件平臺(XSA 文件)相同的位置,并選中包含比特流的選項。
從 Vivado 的工具菜單中啟動 Vitis,然后選擇項目的現有工作區。同樣,我只是在此處修改我上一個項目教程中的現有裸機應用程序,因此請參閱創建新的 Vitis 項目和應用程序。
ZMOD 裸機庫對 ADC 和 DAC 起作用的方式是,它們通過 DMA 交換從 Eclypse 上的 DDR 存儲器填充緩沖區。對于 DAC,Digilent 的原始設計僅啟用了讀取通道(內存映射到流或 MM2S)。由于我們啟用了寫入通道(流到內存映射或 S2MM),因此還需要更新庫以反映現在有一個雙向的 DMA 實例。
從
修改dma.h:
#ifndef DMA_H_
#define DMA_H_
#include
/**
* Direction of a DMA transfer.
*/
enum dma_direction {
DMA_DIRECTION_TX, ///< TX transfer
DMA_DIRECTION_RX, ///< RX transfer
DMA_DIRECTION_TRX ///< TX & RX transfer
};
uint32_t fnInitDMA(uintptr_t addr, enum dma_direction direction, int dmaInterrupt);
void fnDestroyDMA(uintptr_t addr);
int fnOneWayDMATransfer(uintptr_t addr, uint32_t *buf, size_t length);
int fnS2MM_DMATransferCont(uintptr_t addr, uint32_t *buf, size_t transfer_size, int num_transfers);
int fnMM2S_DMATransfer(uintptr_t addr, uint32_t *buf, size_t transfer_size);
uint8_t fnIsDMATransferComplete(uintptr_t addr);
void* fnAllocBuffer(uintptr_t addr, size_t size);
void fnFreeBuffer(uintptr_t addr, void *buf, size_t size);
#endif /* DMA_H_ */
dma.c 中添加了新的 DMA 傳輸函數:
S2MM DMA傳輸功能:
int fnS2MM_DMATransferCont(uintptr_t addr, uint32_t *buf, size_t transfer_size, int num_transfers){
DMAEnv *dmaEnv = (DMAEnv *)addr;
if (!dmaEnv)
return -1;
dmaEnv->complete_flag = 0;
if(dmaEnv->direction != DMA_DIRECTION_TRX){
return -1;
} else {
// S2MM - read in DDS data
// Associate data buffer
writeDMAReg(dmaEnv->base_addr, AXIDMA_REG_ADDR_S2MM_DA, (uint32_t)buf);
// Set DMA RX Run bit, value 1, DMA register
writeDMARegFld(dmaEnv->base_addr, AXIDMA_REGFLD_S2MM_DMACR_RUNSTOP, 1);
for (int i=0;i<num_transfers;i++){
// Start DMA Transfer
writeDMAReg(dmaEnv->base_addr, AXIDMA_REG_ADDR_S2MM_DA_LENGTH, transfer_size);
}
}
return 0;
}
MM2S DMA傳輸功能:
int fnMM2S_DMATransfer(uintptr_t addr, uint32_t *buf, size_t transfer_size){
DMAEnv *dmaEnv = (DMAEnv *)addr;
if (!dmaEnv)
return -1;
dmaEnv->complete_flag = 0;
if(dmaEnv->direction != DMA_DIRECTION_TRX){
return -1;
} else {
// MM2S - write out to DAC
// Associate data buffer
writeDMAReg(dmaEnv->base_addr, AXIDMA_REG_ADDR_MM2S_SA, (uint32_t)buf);
// Set DMA RX Run bit, value 1, DMA register
writeDMARegFld(dmaEnv->base_addr, AXIDMA_REGFLD_MM2S_DMACR_RUNSTOP, 1);
// Start DMA Transfer
writeDMAReg(dmaEnv->base_addr, AXIDMA_REG_ADDR_MM2S_SA_LENGTH, transfer_size);
}
return 0;
}
DMA 函數是從 ZMOD 基礎庫中調用的,因此還需要添加兩個函數。一個啟動 MM2S DMA 事務,一個啟動 S2MM DMA 事務(不要忘記將這些函數原型也添加到 Zmod.h)。
新的 Zmod.cpp 功能:
/**
* Start a DMA S2MM transfer using the transfer length configured previously.
*
* @return 0 on success, any other number on failure
*/
int ZMOD::startS2MMTransferCont(uint32_t* buffer, int num_transfers){
// transfer length is not configured
if (transferSize < 1) {
return ERR_FAIL;
}
return fnS2MM_DMATransferCont(dmaAddr, buffer, transferSize, num_transfers);
}
/**
* Start a DMA MM2S transfer using the transfer length configured previously.
*
* @return 0 on success, any other number on failure
*/
int ZMOD::startMM2STransfer(uint32_t* buffer){
// transfer length is not configured
if (transferSize < 1) {
return ERR_FAIL;
}
return fnMM2S_DMATransfer(dmaAddr, buffer, transferSize);
}
最后,在 DAC ZMOD 特定庫中,需要兩個新函數通過 DMA S2MM 事務從 DDS 編譯器向 DDR 寫入一段正弦波,并將該數據從 DDR 讀取到緩沖區以通過 DMA 發送到 DAC MM2S 交易。還需要修改 DAC 實例的初始化,以指示附加到它的 DMA 現在是雙向的(能夠進行讀取/MM2S 和寫入/S2MM 事務)。
修改了 DAC ZMOD 的 init 實例函數以對 DMA 使用新的雙向類型:
ZMODDAC1411::ZMODDAC1411(uintptr_t baseAddress, uintptr_t dmaAddress, uintptr_t iicAddress, uintptr_t flashAddress, int dmaInterrupt)
: ZMOD(baseAddress, dmaAddress, iicAddress, flashAddress, DMA_DIRECTION_TRX, -1, dmaInterrupt)
{
ZMOD::initCalib(sizeof(CALIBECLYPSEDAC), ZMODDAC1411_CALIB_ID, ZMODDAC1411_CALIB_USER_ADDR, ZMODDAC1411_CALIB_FACT_ADDR);
}
將 DDS 編譯器輸出寫入 DDR 內存的函數:
/*
* Reads in the data values being output by the DDS Compiler and writes them to a
* memory location in the DDR
* @param none
* @return the status: ERR_SUCCESS for success*/
int ZMODDAC1411::readInDDSdata(uint32_t* buffer, size_t &length, int num_transfers){
uint8_t Status;
if(length > ZmodDAC1411_MAX_BUFFER_LEN){
length = ZmodDAC1411_MAX_BUFFER_LEN;
}
// DMA TX transfer length in number of elements
// multiply by the size of the data
setTransferSize(length * sizeof(uint32_t));
// Start DMA Transfer
Status = startS2MMTransferCont(buffer, num_transfers);
if (Status) {
return ERR_FAIL;
}
return ERR_SUCCESS;
}
將 DDS 輸出數據從 DDR 讀取到 DAC ZMOD 緩沖區的函數:
int ZMODDAC1411::sendDDSdataToDAC(uint32_t* buffer, size_t &length){
uint8_t Status;
if(length > ZmodDAC1411_MAX_BUFFER_LEN)
{
length = ZmodDAC1411_MAX_BUFFER_LEN;
}
// DMA TX transfer length in number of elements
// multiply by the size of the data
setTransferSize(length * sizeof(uint32_t));
// Start DMA Transfer
Status = startMM2STransfer(buffer);
if (Status) {
return ERR_FAIL;
}
// // Wait for DMA to Complete transfer
// while(!isDMATransferComplete()) {}
return ERR_SUCCESS;
}
隨著 ZMOD 裸機庫的更新,主要功能相當簡單。主函數首先創建 DAC ZMOD 的實例,然后設置 14 位輸出采樣分頻器和通道一的增益值。然后為緩沖區分配內存,以 DMA 能夠達到的最大長度(0x3fff 或 16384)將數據從 DDS 讀取到 DDR,并分配另一個緩沖區以將數據發送到 DAC。
一個周期的 1MHz 正弦波正從 DDR 內存(相當于 50 個樣本)讀取到第一個緩沖區中。然后將其冗余復制到第二個緩沖區中,直到緩沖區已滿。然后將該緩沖區發送到 DAC 并啟動 DAC。一旦數據成功發送到 DAC,兩個緩沖區的內存都會被釋放,DAC 會無限運行,輸出 1MHz 正弦波。
對于 ADC,我只是重用了我在上一個項目中使用的 Digilent 的 ADC 演示功能。此 ADC 演示功能啟動 ADC 以連續捕獲和無限循環并格式化數據以輸出到 UART 控制臺。
主要功能代碼:
#include
#include
#include
#include "xaxidma.h"
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "./zmodlib/Zmod/zmod.h"
#include "./zmodlib/ZmodADC1410/zmodadc1410.h"
#include "./zmodlib/ZmodDAC1411/zmoddac1411.h"
#include "./zmodlib/Zmod/dma.h"
#define TRANSFER_LEN 0x400
// ZMOD ADC parameters
#define ZMOD_ADC_BASE_ADDR XPAR_AXI_ZMODADC1410_0_S00_AXI_BASEADDR
#define DMA_ADC_BASE_ADDR XPAR_AXI_DMA_ADC_BASEADDR
#define IIC_BASE_ADDR XPAR_PS7_I2C_1_BASEADDR
#define FLASH_ADDR_ADC 0x30
#define ZMOD_ADC_IRQ XPAR_FABRIC_AXI_ZMODADC1410_0_LIRQOUT_INTR
#define DMA_ADC_IRQ XPAR_FABRIC_AXI_DMA_ADC_S2MM_INTROUT_INTR
//ZMOD DAC parameters
#define ZMOD_DAC_BASE_ADDR XPAR_AXI_ZMODDAC1411_V1_0_0_BASEADDR
#define DMA_DAC_BASE_ADDR XPAR_AXI_DMA_DAC_BASEADDR
#define FLASH_ADDR_DAC 0x31
#define DMA_DAC_IRQ XPAR_FABRIC_AXI_DMA_DAC_MM2S_INTROUT_INTR
#define IIC_BASE_ADDR XPAR_PS7_I2C_1_BASEADDR
//DMA for DDS output - XPAR_AXI_DMA_DDS_DEVICE_ID
#define MEM_BASE_ADDR (XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x1000000)
#define TRX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000)
#define TRX_BUFFER_HIGH (MEM_BASE_ADDR + 0x004FFFFF)
/*
* Simple ADC test, puts the ADC in the test mode (ramp),
* performs an acquisition under specific trigger conditions
* and verifies the acquired data to be consistent with these conditions.
*/
void testZMODADC1410Ramp_Auto(){
ZMODADC1410 adcZmod(ZMOD_ADC_BASE_ADDR, DMA_ADC_BASE_ADDR, IIC_BASE_ADDR, FLASH_ADDR_ADC,
ZMOD_ADC_IRQ, DMA_ADC_IRQ);
if(adcZmod.autoTestRamp(1, 0, 0, 4, TRANSFER_LEN) == ERR_SUCCESS){
xil_printf("Success autotest ADC ramp\r\n");
} else {
xil_printf("Error autotest ADC ramp\r\n");
}
}
/*
* Format data contained in the buffer and sends it over UART.
* It displays the acquired value (in mV), raw value (as 14 bits hexadecimal value)
* and time stamp within the buffer (in time units).
* @param padcZmod - pointer to the ZMODADC1410 object
* @param acqBuffer - the buffer containing acquired data
* @param channel - the channel where samples were acquired
* @param gain - the gain for the channel
* @param length - the buffer length to be used
*/
void formatADCDataOverUART(ZMODADC1410 *padcZmod, uint32_t *acqBuffer, uint8_t channel, uint8_t gain, size_t length){
char val_formatted[15];
char time_formatted[15];
uint32_t valBuf;
int16_t valCh;
float val;
xil_printf("New acquisition ------------------------\r\n");
xil_printf("Ch1\tRaw\tTime\t\r\n");
for (size_t i = 0; i < length; i++){
valBuf = acqBuffer[i];
valCh = padcZmod->signedChannelData(channel, valBuf);
val = padcZmod->getVoltFromSignedRaw(valCh, gain);
padcZmod->formatValue(val_formatted, 1000.0*val, "mV");
if (i < 100){
padcZmod->formatValue(time_formatted, i*10, "ns");
} else {
padcZmod->formatValue(time_formatted, (float)(i)/100.0, "us");
}
xil_printf("%s\t%X\t%s\r\n", val_formatted, (uint32_t)(valCh&0x3FFF), time_formatted);
}
}
/*
* Simple ADC test, acquires data and sends it over UART.
* @param channel - the channel where samples will be acquired
* @param gain - the gain for the channel
* @param length - the buffer length to be used
*/
void adcDemo(uint8_t channel, uint8_t gain, size_t length){
ZMODADC1410 adcZmod(ZMOD_ADC_BASE_ADDR, DMA_ADC_BASE_ADDR, IIC_BASE_ADDR, FLASH_ADDR_ADC,
ZMOD_ADC_IRQ, DMA_ADC_IRQ);
uint32_t *acqBuffer;
adcZmod.setGain(channel, gain);
while(1){
acqBuffer = adcZmod.allocChannelsBuffer(length);
adcZmod.acquireImmediatePolling(acqBuffer, length);
formatADCDataOverUART(&adcZmod, acqBuffer, channel, gain, length);
adcZmod.freeChannelsBuffer(acqBuffer, length);
sleep(2);
}
}
int main(){
init_platform();
xil_printf("Eclypse Z7 SDR baseband data generator...\r\n");
// init DAC Zmod
ZMODDAC1411 dacZmod(ZMOD_DAC_BASE_ADDR, DMA_DAC_BASE_ADDR, IIC_BASE_ADDR, FLASH_ADDR_DAC, DMA_DAC_IRQ);
// max buffer length:
size_t length = 0x3fff;
dacZmod.setOutputSampleFrequencyDivider(2);
dacZmod.setGain(0, 1);
int Status = 0;
uint32_t *TrxBufferPtr;
TrxBufferPtr = dacZmod.allocChannelsBuffer(length);
for (int i=0;i<16383;i++){
TrxBufferPtr[i] = 0;
}
uint32_t *acqBufferPtr;
acqBufferPtr = dacZmod.allocChannelsBuffer(length);
Status = dacZmod.readInDDSdata(TrxBufferPtr, length, 1);
if (Status) {
xil_printf("DMA MM2S error!...\r\n");
}
int start_index = 0;
// copy the one period of sine wave into the buffer until its full
while (start_index<16000){
for (int i=0;i<50;i++){
acqBufferPtr[start_index+i] = TrxBufferPtr[i];
}
start_index = start_index + 50;
}
Status = dacZmod.sendDDSdataToDAC(acqBufferPtr, length);
if (Status) {
xil_printf("DMA S2MM error!...\r\n");
}
// start the instrument
dacZmod.start();
// free the buffers since it's been transferred to the DAC
dacZmod.freeChannelsBuffer(TrxBufferPtr, length);
dacZmod.freeChannelsBuffer(acqBufferPtr, length);
// start channel 0 of the ADC collecting infinitely
adcDemo(0, 0, length);
cleanup_platform();
return 0;
}
保存所有文件并構建項目。
要運行應用程序并同時查看 ILA,首先通過右鍵單擊資源管理器窗口中的應用程序名稱并選擇“Program FPGA”來對 Eclypse 進行編程。
請務必將比特流更改為新的比特流。當您單擊比特流字段旁邊的“搜索...”按鈕時,您會看到 Vitis 在項目中檢測到這兩者。
對 Ecylpse 進行編程后,再次右鍵單擊資源管理器窗口中的應用程序名稱并在“調試為”下選擇“在硬件上啟動(單個應用程序調試)”,啟動應用程序的調試運行。
一旦應用程序遇到主函數入口斷點,使用 Vitis 串行終端連接到 Eclypse 的 UART。
在單步執行或運行應用程序之前,切換回 Vivado 并從 Flow Navigator 打開硬件管理器。從 Open Target 選項中選擇自動連接。
如果 ILA 窗口打開并且不存在調試內核,您可能需要從硬件管理器重新編程 FPGA。我發現這是 Vivado 版本 2019.2 的一個錯誤。
在任何感興趣的 AXI 流協議上觸發 ILA 中的觸發器并在 Vitis 中運行應用程序。我監視了 DDS 編譯器的主 AXI 流端口和 DAC 的 DMA 的 AXI 流 S2MM 端口,以便在這個項目上進行調試。
一旦我能夠看到 ILA 中的正弦波環回工作以及 ADC ZMOD 的 UART 控制臺中的打印輸出,我決定啟動我的老式頻譜分析儀,它位于我書柜的頂部架子上,看看 1MHz 正弦波是什么波實際上看起來像是從 DAC 端口的 SMA 端口出來的。
我的頻譜分析儀實際上是用于電視維修的,因此射頻輸入為 75 歐姆,因此使用 75 至 50 歐姆的巴倫和一些適配器電纜將其 BNC 輸入轉換為 ZMOD 的 SMA 端口并獲得日蝕相連。
正如你所看到的,對于一個應該是單一頻率的純連續正弦波,它與一系列額外的頻率分量相當混亂。我確信這與我荒謬的長 SMA 電纜和破解連接轉換有關。該頻譜分析儀自 1984 年以來還沒有進行過正確校準,但使用它仍然可以很好地看到 DAC ZMOD 確實以 1MHz 的頻率發出了模擬信號。
我發現我擁有的 SMA 電纜比我擁有的任何 USB 電纜都長得多,所以我最終將信號分析儀留在了我的書架頂部,并將 Eclypse 板放在我的辦公桌窩上,放在我的電腦上。
為什么我擁有比 USB 電纜更長的 SMA 電纜???好問題。我自己剛剛發現了這個異常。
總體而言,可以以非常簡單的方式修改設計,使 DDS 編譯器的相位增量和偏移輸入可編程,并最終使用 ZMOD 制作出 Eclypse 的基帶數據生成器。我會把它保存到另一個項目教程中!
- 利用DAC實現正弦波輸出
- 使用單片機和DAC0832產生正弦波
- 基于STM32f103使用的DAC正弦波 113次下載
- 用超純正弦波振蕩器測試18位ADC
- 基于AD7226四進制DAC的AN-321:三相正弦波產生
- DAC輸出正弦波的實驗程序和工程文件免費下載 7次下載
- 實用電源技術叢書之正弦波逆變器 170次下載
- DAC—輸出正弦波 74次下載
- 正弦波逆變器基礎知識及應用 91次下載
- 基于SPWM的正弦波設計與實現 19次下載
- 正弦波數據生成器(正弦波表) 141次下載
- 正弦波發生電路基礎
- 使用集成運放LM324制作正弦波發生器
- 使用集成運放LM324制作正弦波發生器
- dsp正弦波發生器
- 示波器怎么調正弦波 1004次閱讀
- 方波 正弦波 三角波信號是如何產生的? 4655次閱讀
- 有源晶振的波形是正弦波嗎?怎么測量? 2283次閱讀
- 分享三個簡單的正弦波發生器電路 1.9w次閱讀
- 正弦波逆變器的判定方法_如何將正弦波變成方波 1.5w次閱讀
- 正弦波逆變器和修正波有什么不同 2.5w次閱讀
- 正弦波逆變器制作過程詳解 2w次閱讀
- 正弦波濾波器原理_正弦波濾波器的作用 2.6w次閱讀
- 正弦波逆變器原理_正弦波逆變器的優缺點 3.3w次閱讀
- 通過觀察正弦波和無源元件來研究相位的概念 6131次閱讀
- 正弦波逆變器介紹_正弦波逆變器工作原理 4.9w次閱讀
- 純正弦波逆變器哪個好_純正弦波逆變器排行榜 9.2w次閱讀
- lc正弦波振蕩電路詳解_LC正弦波振蕩電路振蕩的判斷方法 4.3w次閱讀
- 正弦波信號發生器基本原理與設計 8.1w次閱讀
- 什么是正弦波逆變器_正弦波逆變器的工作原理及相關電路圖 8.6w次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數據手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關電源設計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數字電路基礎pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅動電路設計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多