本文基于之前的Quartus工程创建教程:8-LTC2308控制器设计——Quartus工程创建
1.提前在你的工程目录下新建一个ip文件夹,然后在ip文件夹下新建一个ADC_LTC2308_FIFO文件夹:

2. 点击View——Utility Windows——IP Catalog打开IP库:

3. 在IP Catalog中键入fifo进行搜索找到FIFO IP,双击它:

4. 在Save IP Variation界面点击箭头处:

5. 将文件路径设置到你工作目录下~\DE10_Standard_ADC\ip\ADC_LTC2308_FIFO, 给这个FIFO模块命名为adc_data_fifo,然后点击保存:

6. 然后点击OK:

7. 随即跳出如下对话框,FIFO位宽设置为12bit,深度设置为2048个word。设置异步读写时钟,然后点击Next:

8. 点选同步复位复选框如下:

9. 接下来一直点击Next,直到如下界面出现,然后点击Finish结束FIFO 模块的设置:

10. 系统会弹出对话框问是否将刚才配置好的FIFO IP 添加到工程,点击Yes:

11. 这时候可以看到~\DE10_Standard_ADC\ip\ADC_LTC2308_FIFO路径下产生了这些文件:

12. 按照8-LTC2308控制器设计——Quartus工程创建里面创建top-level文件的方法继续创建adc_ltc2308.v和adc_ltc2308_fifo.v两个.v文件(文件代码见本文附录)并放在如下路径:

13. 点击Platform Designer按钮:

14.在弹出来的窗口上选择File——New Component...

15.在子页面Component Type处填写如下信息:

16.然后点击子页面Files,点击Add File..., 选择adc_data_fifo.v、adc_ltc2308.v和adc_ltc2308_fifo.v三个文件,点击打开:

17.系统自动将adc_data_fifo.v文件默认为自定义IP的顶层文件了,而真正的顶层文件是adc_ltc2308_fifo.v,此时需要点击箭头处,然后将Top-level File复选框选上,然后点击OK:

18. 打开Signals&Interfaces可以看到这个窗口还是空白,没有任何端口:

19. 返回到Files窗口,点击Analyze Synthesis Files 对文件进行分析和综合,Analyze Synthesis Files Completed窗口点击Close:

注意,这里要保证Analyze Synthesis Files Completed窗口没有error出现,至于下面Messages窗口出现error,可以暂时不管,后面的操作会解决这些问题的。
20. 此时再返回到Signals&Interfaces可以看到这个窗口里有很多端口且被分类了,但是这些端口是系统自动识别的,还需要我们手动去调整:

21.点击add interface,选择Clock Input:

22. 鼠标选中avalon_slave_0下面的slave_clk信号,将其拖拽到clcok_sink下面:

23. slave_clk 信号的Signal Type 处选择clk:

24. 再次点击add interface,选择Clock Input,然后将clock_sink_1改名字为clock_sink_adc, 并把adc_clk信号拖拽到clock_sink_adc下面,同样,adc_clk 信号的Signal Type 处选择clk:

25. 点击add interface,选择Reset Input:

26. 将slave_reset_n信号拖拽到reset_sink接口下面, 并在Signal Type处选择reset_n:

27.将slave_addr信号拖拽到slave接口处,Signal Type处选择address :

28. 点击avalon_slave接口,在Type处下拉菜单选择Conduit接口:

29. 将Conduit接口下面的ADC_CONVST、ADC_SCK、ADC_SDI、ADC_SDO接口的Signal Type改成跟接口一样的名称:

30. 点击slave接口,将Associated Clock设置为clock_sink, 将Associated Reset设置为reset_sink:

31. 这时候返回子页面Block Symbol 查看到如下接口框图,最后点击Finish完成自定义IP的封装:

31.点击Yes,Save来保存自定义IP的所有设置到adc_ltc2308_hw.tcl文件当中:

32. 这时候再返回到Platform Designer 界面可以看到自定义IP adc_ltc2308已经在IP Catalog里面显示出来了, 接下来就可以在Platform Designer 里面调用这个IP了。

如今自定义IP 都是默认使用动态地址对齐,本案例的Nios 程序使用静态地址对齐(每个寄存器在Avalon总线上占4个字节的地址),此时需要在hw.tcl脚本里加上一句话:set_interface_property slave addressAlignment NATIVE

附录
adc_ltc2308.v文件代码
如下:
module adc_ltc2308( clk, // max 40mhz // start measure measure_start, // posedge triggle measure_ch, measure_done, measure_dataread, // adc interface ADC_CONVST, ADC_SCK, ADC_SDI, ADC_SDO ); input clk; // start measure input measure_start; input [2:0] measure_ch; output reg measure_done; output [11:0] measure_dataread; output ADC_CONVST; output ADC_SCK; output reg ADC_SDI; input ADC_SDO; ///////////////////////////////// // Timing definition // using 40MHz clock // to acheive fsample = 500KHz // ntcyc = 2us / 25ns = 80 `define DATA_BITS_NUM 12 `define CMD_BITS_NUM 6 `define CH_NUM 8 `define tWHCONV 3 // CONVST High Time, min 20 ns `define tCONV 64 //52 // tCONV: type 1.3 us, MAX 1.6 us, 1600/25(assumed clk is 40mhz)=64 -> 1.3us/25ns = 52 // set 64 for suite for 1.6 us max // +12 //data `define tHCONVST 320 // 12 // here set 320( fsample = 100KHz) for if ADC input impedance is high, see below // If the source impedance of the driving circuit is low, the ADC inputs can be driven directly. //Otherwise, more acquisition time should be allowed for a source with higher impedance. // for acheiving 500KHz fmax. set n cyc = 80. `define tCONVST_HIGH_START 0 `define tCONVST_HIGH_END (`tCONVST_HIGH_START+`tWHCONV) `define tCONFIG_START (`tCONVST_HIGH_END) `define tCONFIG_END (`tCLK_START+`CMD_BITS_NUM - 1) `define tCLK_START (`tCONVST_HIGH_START+`tCONV) `define tCLK_END (`tCLK_START+`DATA_BITS_NUM) `define tDONE (`tCLK_END+`tHCONVST) // create triggle message: reset_n reg pre_measure_start; always @ (posedge clk) begin pre_measure_start <= measure_start; end wire reset_n; assign reset_n = (~pre_measure_start & measure_start)?1'b0:1'b1; // tick reg [15:0] tick; always @ (posedge clk or negedge reset_n) begin if (~reset_n) tick <= 0; else if (tick < `tDONE) tick <= tick + 1; end ///////////////////////////////// // ADC_CONVST assign ADC_CONVST = (tick >= `tCONVST_HIGH_START && tick < `tCONVST_HIGH_END)?1'b1:1'b0; ///////////////////////////////// // ADC_SCK reg clk_enable; // must sync to clk in clk low always @ (negedge clk or negedge reset_n) begin if (~reset_n) clk_enable <= 1'b0; else if ((tick >= `tCLK_START && tick < `tCLK_END)) clk_enable <= 1'b1; else clk_enable <= 1'b0; end assign ADC_SCK = clk_enable?clk:1'b0; /////////////////////////////// // read data reg [(`DATA_BITS_NUM-1):0] read_data; reg [3:0] write_pos; assign measure_dataread = read_data; always @ (negedge clk or negedge reset_n) begin if (~reset_n) begin read_data <= 0; write_pos <= `DATA_BITS_NUM-1; end else if (clk_enable) begin read_data[write_pos] <= ADC_SDO; write_pos <= write_pos - 1; end end /////////////////////////////// // measure done wire read_ch_done; assign read_ch_done = (tick == `tDONE)?1'b1:1'b0; always @ (posedge clk or negedge reset_n) begin if (~reset_n) measure_done <= 1'b0; else if (read_ch_done) measure_done <= 1'b1; end /////////////////////////////// // adc channel config // pre-build config command reg [(`CMD_BITS_NUM-1):0] config_cmd; `define UNI_MODE 1'b1 //1: Unipolar, 0:Bipolar `define SLP_MODE 1'b0 //1: enable sleep always @(negedge reset_n) begin if (~reset_n) begin case (measure_ch) 0 : config_cmd <= {4'h8, `UNI_MODE, `SLP_MODE}; 1 : config_cmd <= {4'hC, `UNI_MODE, `SLP_MODE}; 2 : config_cmd <= {4'h9, `UNI_MODE, `SLP_MODE}; 3 : config_cmd <= {4'hD, `UNI_MODE, `SLP_MODE}; 4 : config_cmd <= {4'hA, `UNI_MODE, `SLP_MODE}; 5 : config_cmd <= {4'hE, `UNI_MODE, `SLP_MODE}; 6 : config_cmd <= {4'hB, `UNI_MODE, `SLP_MODE}; 7 : config_cmd <= {4'hF, `UNI_MODE, `SLP_MODE}; default : config_cmd <= {4'hF, 2'b00}; endcase end end // serial config command to adc chip wire config_init; wire config_enable; wire config_done; reg [2:0] sdi_index; assign config_init = (tick == `tCONFIG_START)?1'b1:1'b0; assign config_enable = (tick > `tCLK_START && tick <= `tCONFIG_END)?1'b1:1'b0; // > because this is negative edge triggle assign config_done = (tick > `tCONFIG_END)?1'b1:1'b0; always @(negedge clk) begin if (config_init) begin ADC_SDI <= config_cmd[`CMD_BITS_NUM-1]; sdi_index <= `CMD_BITS_NUM-2; end else if (config_enable) begin ADC_SDI <= config_cmd[sdi_index]; sdi_index <= sdi_index - 1; end else if (config_done) ADC_SDI <= 1'b0; // end endmodule
adc_ltc2308_fifo.v文件代码
如下:
/* note for avalon interface bus type: nagtive read legacy = 0 (to consistent to FIFO) */ module adc_ltc2308_fifo( // avalon slave port slave_clk,//总线时钟,100M,同步所有总线操作 slave_reset_n,//复位信号,低有效 slave_chipselect_n,//片选信号,低有效 slave_addr,//地址总线,单bit地址空间 slave_read_n,//读使能,低有效 slave_write_n,//写使能,低有效 slave_readdata,//输出数据总线,16bit slave_writedata,//输入数据总线,16bit adc_clk, // max 40mhz // adc interface ADC_CONVST, ADC_SCK, ADC_SDI, ADC_SDO ); // avalon slave port input slave_clk; input slave_reset_n; input slave_chipselect_n; input slave_addr; input slave_read_n; input slave_write_n; output reg [15:0] slave_readdata; input [15:0] slave_writedata; input adc_clk; output ADC_CONVST; output ADC_SCK; output ADC_SDI; input ADC_SDO; //////////////////////////////////// // avalon slave port `define WRITE_REG_START_CH 0//常量定义,用于标识通道选择寄存器和启动转换标志寄存器的地址 `define WRITE_REG_MEASURE_NUM 1//常量定义,用于标识采样次数寄存器地址 // write for control reg measure_fifo_start;//存储启动转换标志的寄存器 reg [11:0] measure_fifo_num;//存储采样次数的寄存器 reg [2:0] measure_fifo_ch;//存储通道选择的寄存器 //根据地址和控制信号,将写入数据存储到相应的寄存器中 always @ (posedge slave_clk or negedge slave_reset_n) begin if (~slave_reset_n) measure_fifo_start <= 1'b0; else if (~slave_chipselect_n && ~slave_write_n && slave_addr == `WRITE_REG_START_CH) {measure_fifo_ch, measure_fifo_start} <= slave_writedata[3:0]; else if (~slave_chipselect_n && ~slave_write_n && slave_addr == `WRITE_REG_MEASURE_NUM) measure_fifo_num <= slave_writedata; end /////////////////////// // read `define READ_REG_MEASURE_DONE 0//常量定义,用于标识寄存器地址 `define READ_REG_ADC_VALUE 1 wire slave_read_status; wire slave_read_data; //根据地址和控制信号,将相应数据输出到读数据端口 assign slave_read_status = (~slave_chipselect_n && ~slave_read_n && slave_addr == `READ_REG_MEASURE_DONE) ?1'b1:1'b0; assign slave_read_data = (~slave_chipselect_n && ~slave_read_n && slave_addr == `READ_REG_ADC_VALUE) ?1'b1:1'b0; reg measure_fifo_done; always @ (posedge slave_clk) begin if (slave_read_status) slave_readdata <= {11'b0, measure_fifo_done};//转换完成状态指示 else if (slave_read_data) //读取FIFO中的转换结果 slave_readdata <= fifo_q; end reg pre_slave_read_data; always @ (posedge slave_clk or negedge slave_reset_n) begin if (~slave_reset_n) pre_slave_read_data <= 1'b0; else pre_slave_read_data <= slave_read_data; end // read ack for adc data. (note. Slave_read_data is read lency=2, so slave_read_data is assert two clock) assign fifo_rdreq = (pre_slave_read_data & slave_read_data)?1'b1:1'b0; //////////////////////////////////// // create triggle message: adc_reset_n //生成ADC复位信号,当测量开始时复位ADC reg pre_measure_fifo_start; always @ (posedge adc_clk) begin pre_measure_fifo_start <= measure_fifo_start; end wire adc_reset_n; assign adc_reset_n = (~pre_measure_fifo_start & measure_fifo_start)?1'b0:1'b1; //////////////////////////////////// // control measure_start reg [11:0] measure_count; reg config_first; reg wait_measure_done; reg measure_start; wire measure_done; wire [11:0] measure_dataread; //控制测量过程的启动、进行和结束 always @ (posedge adc_clk or negedge adc_reset_n) begin if (~adc_reset_n) begin measure_start <= 1'b0; config_first <= 1'b1;//复位时: Reset config_first 被置为 1,表示系统处于首次配置状态。 measure_count <= 0; measure_fifo_done <= 1'b0; wait_measure_done <= 1'b0; end else if (~measure_fifo_done & ~measure_start & ~wait_measure_done) begin measure_start <= 1'b1; wait_measure_done <= 1'b1; end else if (wait_measure_done) // && measure_start) begin measure_start <= 1'b0; if (measure_done) begin if (config_first)// 当config_first = 1 时,系统会 跳过第一次转换的数据存储 config_first <= 1'b0; else//当 config_first = 0 时,系统进入正常数据采集模式,每次转换完成后将数据写入 FIFO begin // read data and save into fifo if (measure_count < measure_fifo_num) // && ~fifo_wrfull) begin measure_count <= measure_count + 1; wait_measure_done <= 1'b0; end else measure_fifo_done <= 1'b1; end end end end // write data into fifo reg pre_measure_done; always @ (posedge adc_clk or negedge adc_reset_n) begin if (~adc_reset_n) pre_measure_done <= 1'b0; else pre_measure_done <= measure_done; end assign fifo_wrreq = (~pre_measure_done & measure_done & ~config_first)?1'b1:1'b0; /////////////////////////////////////// // SPI //实例化LTC2308 ADC模块 adc_ltc2308 adc_ltc2308_inst( .clk(adc_clk), // max 40mhz // start measure .measure_start(measure_start), // posedge triggle .measure_done(measure_done),//一次采样转换完毕指示信号 .measure_ch(measure_fifo_ch), .measure_dataread(measure_dataread), //ADC输出的数据 // adc interface .ADC_CONVST(ADC_CONVST), .ADC_SCK(ADC_SCK), .ADC_SDI(ADC_SDI), .ADC_SDO(ADC_SDO) ); /////////////////////////////////////// // FIFO wire fifo_wrfull; wire fifo_rdempty; wire fifo_wrreq; wire [11:0] fifo_q; wire fifo_rdreq; //实例化FIFO模块,用于存储ADC读取的数据 adc_data_fifo adc_data_fifo_inst( .aclr(~adc_reset_n), .data(measure_dataread), .rdclk(slave_clk), .rdreq(fifo_rdreq), .wrclk(adc_clk), .wrreq(fifo_wrreq), .q(fifo_q), .rdempty(fifo_rdempty), .wrfull(fifo_wrfull) ); endmodule
往期推荐阅读:
浙公网安备 33010602011771号