首页
📁归档
⏳时光机
📫留言
🚩友链
💰资助名单
推荐
🎧音乐
🏜️ 壁纸
❤ 捐助
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
19,480 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
10,313 阅读
3
【高数】形心计算公式讲解大全
8,837 阅读
4
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
7,565 阅读
5
Vivado-FPGA Verilog烧写固化教程
7,088 阅读
🪶微语&随笔
励志美文
我的随笔
写作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
21(共147篇)
找到
147
篇与
21
相关的结果
- 第 4 页
2022-12-25
【FPGA】【SPI】线性序列机与串行接口 ADC 驱动设计与验证
本章导读 模数转换器即 A/D 转换器,或简称 ADC(Analog to Digital Conver),通常是指一个将模拟信号转变为数字信号的电子元件。通常的模数转换器是把经过与标准量比较处理后的模拟量转换成以二进制数值表示的离散信号的转换器。 模数转换器的种类很多,按工作原理的不同,可分成间接 ADC 和直接 ADC。间接 ADC是先将输入模拟电压转换成时间或频率,然后再把这些中间量转换成数字量,常用的有双积分型 ADC。直接 ADC 则直接转换成数字量,常用的有并联比较型 ADC 和逐次逼近型 ADC。 并联比较型 ADC:采用各量级同时并行比较,各位输出码也是同时并行产生,所以转换速度快。并联比较型 ADC 的缺点是成本高、功耗大。 逐次逼近型 ADC:它产生一系列比较电压 VR,但它是逐个产生比较电压,逐次与输入电压分别比较,以逐渐逼近的方式进行模数转换。它比并联比较型 ADC 的转换速度慢,比双分积型 ADC 要快得多,属于中速 ADC 器件。 双积分型 ADC:它先对输入采样电压和基准电压进行两次积分,获得与采样电压平均值成正比的时间间隔,同时用计数器对标准时钟脉冲计数。它的优点是抗干扰能力强,稳定性好;主要缺点是转换速度低。 本节以 ADC128S022 为例介绍 ADC 的工作原理及时序图解释,并用线性序列机来描述时序图进而正确驱动此类设备。本实验中,直接使用 AD/DA 模块上的 DAC 模块的输出直接接到 ADC 模块的输入上,通过控制 DAC 输出不同电压值,然后比较 DAC 的输出与 ADC采样到的值的大小,来评估 ADC 驱动的正确性。 ADC 芯片概述及电路设计 ADC128S022 型 ADC 内部工作原理 在 AC620 开发板上使用的模数转换器为逐次逼近型的低功耗芯片 ADC128S022,其具有 8 通道以及 12 位的分辨率。电源采用独立的模拟供电以及数字供电,其中模拟电源 VA输入范围为 2.7V~5.25V,数字电源 VD 输入范围为 2.7V~VA。其与外部通信支持多种接口如:SPI、QSPI、MICROWIRE 以及通用的 DSP 接口。转换速度在 50kps~200kps,典型情况下当3V 供电时功耗为 1.2mW,5V 供电时为 7.5mW。 图片 本款 ADC 为 12 位分辨率,因此 1bit 代表的电压值即为 VA/4096。手册中同时指出,当模拟输入电压低于 VA/8192 时,输出数据即为 0000_0000_0000b。同理由于芯片本身内部构造当输出数 0000_0000_0000b 变为 0000_0000_0001b 时,实际输入电压变化为 VA/8192 而不是 VA/4096。当输入电压大于等于 VA-1.5* VA/4096,输出数据即为 1111_1111_1111b。 ADC128S022 型 ADC 芯片引脚功能 ADC128S022 芯片引脚及功能描述如表 25.1 所示。 图片 ADC128S022 电路设计 ADC128S022 部分电路图如图 25.2 所示。这里外接了一个抗混叠低通滤波器,其作用是为了避免输入信号的高频成分在 ADC 的基带中引起混叠。数字电源与模拟电源电压均为3.3V,且数字电源与模拟电源之间串联磁珠用于抑制电磁干扰。 图片 传输特性 图片 ADC128S022 型 ADC 接口时序 微控制器与 ADC128S022 的接口如图 25.3 所示,该接口使用标准的 SPI 接口,因此可以直接连接到微控制器的片上 SPI。对于 FPGA 来说,则可以使用逻辑按照 SPI 时序搭建控制电路,以实现对 ADC128S022 的控制。 图片 ADC128S022 通过 SPI 接口与控制器进行通信的时序图如图 所示。一个串行帧开始于CS̅̅̅的下降沿,结束于CS̅̅̅的上升沿。一帧包含 16 个上升沿 SCLK。ADC 的 DOUT 引脚当CS̅̅̅为高时代表空闲状态,当为低时为传输状态。也就是相当于CS̅̅̅可以充当输出使能。在CS̅̅̅为高时 SCLK 默认高。在头三个 SCLK 的循环,ADC 处于采样模式(track)。在接下来的 13 个循环为保持模式(hold),转换完成并且完成数据输出。下降沿 1~4 为前导零,5~16 为输出转换结果。如果在持续测量模式中,ADC 在 N(16) 个 SCLK 的上升沿会自动进去采样模式,在N16+4 个下降沿进入保持/转换模式。 图片 图片 进入采样模式有三种方式,第一种当 SCLK 为高电平时 CS 变为低,当 SCLK 第一个下降沿到来时便进入采样模式,如时序图所示;第二种当 SCLK 为低电平时CS变低,自动进入采样模式,CS的下降沿即视为 SCLK 的第一个下降沿;第三种 SCLK 与CS一同变为低,这样对于两信号上升沿没有时序约束,但对于其下降沿有要求。 在 DIN 的 8 个控制寄存器,每一位代表的含义如表 25.2 所示。 图片 其中 ADD[2:0]代表的输入通道选择如表 25.3 所示。 图片 图片 ADC128S022 接口时序设计 经查阅手册可知器件工作频率 SCLK 推荐范围为 0.8~3.2MHz,这里定义其工作频率为1.92MHz(周期为 520ns)。设置一个两倍于 SCLK 的采样时钟 SCLK2X,使用 50M 系统时钟十三分频而来即 SCLK2X 为 3.84MHz。针对 SCLK2X 进行计数来确定图 25.4 中各个信号的状态。结合前面 ADC 的接口时序图,按照线性序列机的设计思路,可以整理得到每个信号发生变化时对应的时刻以及此时对应的计数器的值。表25.4为依照线性序列机的设计思想, 整理得到的每个信号发生变化时对应的时刻以及此时对应的计数器的值。其中 CS_N 为芯片 状态标志信号,SCLK 为芯片时钟输入脚,DIN 为芯片串行数据输入,DOUT 为芯片串行数据输出。 图片 图片 线性序列机计数器的控制逻辑判断依据,如表 4.17.6 所示。 图片 以上就是通过线性序列机设计接口时序的一个典型案例,可以看到,线性序列机可以大大简化设计思路。线性序列机的设计思想就是使用一个计数器不断计数,由于每个计数值都会对应一个时间,那么当该时间符合需要操作信号的时刻时,就对该信号进行操作。这样,就能够轻松的设计出各种时序接口了。 基于线性序列机的 ADC 驱动设计 模块接口设计 ADC128S022 接口逻辑模块图如图 25.5 所示。 图片 每个端口功能描述如表 25.5 所示。 图片 图片 在每次使能转换的时候,寄存当前状态通道选择 Channel 的值,防止在转换过程中该值发生变化对转换过程产生影响。 reg [2:0]r_Channel; //通道选择内部寄存器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) r_Channel <= 3'd0; else if(En_Conv) r_Channel <= Channel; else r_Channel <= r_Channel;生成使能信号,当输入使能信号有效后便将使能信号 en 置 1,当转换完成信号有效时便将其重新置 0。 reg en; //转换使能信号 always@(posedge Clk or negedge Rst_n) if(!Rst_n) en <= 1'b0; else if(En_Conv) en <= 1'b1; else if(Conv_Done) en <= 1'b0; else en <= en;在数据手册中SCLK的频率范围为0.8~3.2MHz。这里为了方便适配不同的频率需求率,设置了一个可调的计数器。根据表中可以看出,需要根据计数器的值周期性的产生 SCLK时钟信号,这里可以将计数器的值等倍数放大,形成过采样。这里产生一个两倍于 SCLK 的信号,命名为 SCLK2X。 首先编写分频计数器,时钟 SCLK2X 的计数器。 reg [7:0]DIV_CNT;//分频计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) DIV_CNT <= 8'd0; else if(en)begin if(DIV_CNT == (DIV_PARAM - 1'b1)) //时钟分频设置 DIV_CNT <= 8'd0; else DIV_CNT <= DIV_CNT + 1'b1; end else DIV_CNT <= 8'd0;根据使能信号以及计数器状态生成 SCLK2X 时钟。 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK2X <= 1'b0; else if(en && (DIV_CNT == (DIV_PARAM - 1'b1))) SCLK2X <= 1'b1; else SCLK2X <= 1'b0;每当使能转换后,对 SCLK2X 时钟进行计数。 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK_GEN_CNT <= 6'd0; else if(SCLK2X && en)begin if(SCLK_GEN_CNT == 6'd33) SCLK_GEN_CNT <= 6'd0; else SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'd1; end else SCLK_GEN_CNT <= SCLK_GEN_CNT;根据 SCLK2X 计数器的值来确认工作状态以及数据传输进程。 reg [11:0]r_data; //转换结果读取内部寄存器 reg [5:0]SCLK_GEN_CNT;//SCLK2X 计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin SCLK <= 1'b1;CS_N <= 1'b1;DIN <= 1'b1; end else if(en) begin if(SCLK2X)begin case(SCLK_GEN_CNT) 6'd0:begin CS_N <= 1'b0; end 6'd1:begin SCLK <= 1'b0; DIN <= 1'b0; end 6'd2:begin SCLK <= 1'b1; end 6'd3:begin SCLK <= 1'b0; end 6'd4:begin SCLK <= 1'b1; end 6'd5:begin SCLK <= 1'b0; DIN <= r_Channel[2];end 6'd6:begin SCLK <= 1'b1; end 6'd7:begin SCLK <= 1'b0; DIN <= r_Channel[1];end 6'd8:begin SCLK <= 1'b1; end 6'd9:begin SCLK <= 1'b0; DIN <= r_Channel[0];end 6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd 30,6'd32: begin SCLK <= 1'b1; r_data <= {r_data[10:0], DOUT}; end 6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,6'd27,6'd29,6'd 31: begin SCLK <= 1'b0; end 6'd33:begin CS_N <= 1'b1; end//将转换结果输出 default:begin CS_N <= 1'b1; end endcase end else ; end else CS_N <= 1'b1;一次转换结束的标志,即 en && SCLK2X && (SCLK_GEN_CNT == 6'd33)为真,并产生一个高脉冲的转换完成标志信号 Conv_Done。一次转换结束后将内部寄存器 r_data 的数据输出到输出端 Data 上。 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin Data <= 12'd0; Conv_Done <= 1'b0; end else if(en && SCLK2X && (SCLK_GEN_CNT == 6'd33))begin Data <= r_data; Conv_Done <= 1'b1; end else begin Data <= Data; Conv_Done <= 1'b0; endADC 工作状态,在手册中看出 CS_N 在工作时为低电平,因此直接将此信号作为工作状态指示。 assign ADC_State = CS_N;仿真及板级测试 为了测试模块功能,模拟 ADC 芯片的输出。这里用 Sin3e 产生一个正弦波文件,位宽12 位,个数为 4096,并以 sin_12bit.txt 保存当前工程下的 simulation 目录下的 modelsim 文件夹。 这样就需要将产生的数据,发送到 ADC 驱动模块的输入线 DOUT 上,这里使用系统任务$readmemb,其可以用来从文件中读取数据到存储器中。其格式有以下三种: $readmemb("<数据文件名>",<存贮器名>); $readmemb("<数据文件名>",<存贮器名>,<起始地址>); $readmemb("<数据文件名>",<存贮器名>,<起始地址>,<结束地址>。 首先定义文件存放位置。 `define sin_data_file "./sin_12bit.txt"定义 4096 个 12 位的存储单元,然后将波形数据读取到存储器中。 reg[11:0] memory[4095:0];//测试波形数据存储空间 initial $readmemh(`sin_data_file,memory);在测试时将这些数据整体循环三次,进行验证。其中 gene_DOUT(memory[address]);依次将存储器中存储的波形数据读出,按照 ADC 芯片转换结果输出方式的送到 DOUT 信号线上。 integer i; initial begin Rst_n = 0;Channel = 0; En_Conv = 0; DOUT = 0;address = 0; #101; Rst_n = 1; #100; Channel = 5; for(i=0;i<3;i=i+1)begin for(address=0;address<4095;address=address+1)begin En_Conv = 1; #20; En_Conv = 0; gene_DOUT(memory[address]); @(posedge Conv_Done); //等待转换完成信号 #200; end end #20000; $stop; end将存储空间的数据按照 ADC 的数据输出格式,发送到 DOUT 信号线上,供控制模块采集。 task gene_DOUT; input [15:0]vdata; reg [4:0]cnt; begin cnt = 0; wait(!CS_N); while(cnt<16)begin @(negedge SCLK) DOUT = vdata[15-cnt]; cnt = cnt + 1'b1; end end endtask全编译后进行仿真,可看出此次信号较多。这里首先查看与 ADC 相关的使能信号、状态标志信号以及 SCLK 时钟信号等。选中以上这些信号,可以看出,每当外界输入一个周期的 En_Conv 转换信号,模块内部使能信号 en 就置高,一直到转换完成标志信号 Conv_Done有效再置 0。当 SLK2X 计数器值为 33d 且当 SCLK2X 为高时,便输出一个时钟周期的高脉冲信号,标志着本次转换过程结束。同时 SCLK2X 时钟为 ADC 工作时钟 SCLK 的两倍。以上各信号符合既定的设计。 图片 DIN 为 FPGA 控制 ADC 通道选择的信号,这里选择的通道五也就是 ADD[2:0]=101b,可以看出图每个测量循环中发送 DIN 状态均一致。 图片 查看 DOUT 串行数据输出信号,在每个 SCLK 上升沿 DOUT 均会输出一位数据。在第一个转换过程中,DOUT 首先输出 4 个前导 0,然后依次输出 1000_0000_0000b(MSB),也就是在激励文件中例化的正弦波的第一个数据 800h。并在一次转换完成后,输出并行数据 Data。符合既定设计,可以自行分析第二个转换过程。 图片 查看 CS_N 与 ADC_State 工作状态标志信号,可看出当 ADC 处于转换时为低电平,空闲时为高电平,符合设计要求。 图片 将并行输出数据 Data 设置为模拟形式,主要参数如图 25.10 所示,其中波形所占高度可根据实际情况自行设计,此处暂定 100。 图片 重启仿真后可看出数据输入为 3 个周期的正弦波也就是采样正确,符合既定设计。这里如不重启仿真可能会出现如图 25.12 所示的波形影响观测,此为仿真软件本身问题,可不深究。 图片 为了进行板级仿真,新建测试 ADC_test 顶层文件,在顶层文件中例化 ADC 以及 DAC驱动,并设置好相关使能以及标志信号。再分别生成两个 ISSP 文件,其中驱动 DAC 电压数据的命名为 ISSP_DAC(源位宽为 16),采集 ADC 电压以及通道设置的为 ISSP_ADC(源位宽为 3,探针位宽为 12)。分配引脚并全编译后,下载程序,并启动 ISSP。 1.DAC 的 DA 输出端连接 ADC 的 A0 输入端。如图 25.13 所示,使得 DAC 输出电压为0,可测得电压为 0。 图片 2.DAC 的 DA 输出端连接 ADC 的 A0 输入端。此时更新 DB 的输出值,如图 25.14,可以看出 A0 测量数据保持 0 不变。 图片 3.DAC 的 DB 输出端接 ADC 的 A1 输入端。此时更新 DB 值,其理论输出电压为7FF/FFF 4.096=2.048V ,如图 25.15 所示,使能 A1 测量端可测得输入电压为2535/40963.3=2.04V,在误差允许范围内。 图片 DAC的DB输出端接ADC的 A1 输入端。此时更新 DB 值,其理论输出电压为 BD3/FFF 4.096=3.072V,如图 25.16 所示,使能 A1 测 量 端 可 测 得 输 入 电 压 为 3815/4096 3.3=2.07V,在误差允许范围内。 图片 同理可测试的其他 DAC 输出电压以及 ADC 输入测量电压值。此处换算时需注意,DAC电路输出电压范围为 0~4V,ADC 电路测量电压范围为 0~3.3V。 工程代码 图片 在我们设计的驱动模块中,需要给ADC芯片三个控制信号SCLK、CS_N、DIN,还需要有接收ADC转化结果的ADC_OUT。ADC芯片的选址信号通过Channel端给到ADC_driver中,然后通过ADC_DIN给到芯片中,用于选择采集模拟信号的通道。12位宽的Data输出给外部的FIFO缓存。 module adc128s022( Clk, Rst_n, Channel, Data, En_Conv, Conv_Done, ADC_State, DIV_PARAM, ADC_SCLK, ADC_DOUT, ADC_DIN, ADC_CS_N ); input Clk; //输入时钟 input Rst_n; //复位输入,低电平复位 input [2:0]Channel; //ADC转换通道选择 output reg [11:0]Data; //ADC转换结果 input En_Conv; //使能单次转换,该信号为单周期有效,高脉冲使能一次转换 output reg Conv_Done; //转换完成信号,完成转换后产生一个时钟周期的高脉冲 output ADC_State; //ADC工作状态,ADC处于转换时为低电平,空闲时为高电平 input [7:0]DIV_PARAM; //时钟分频设置,实际SCLK时钟 频率 = fclk / (DIV_PARAM * 2) output reg ADC_SCLK; //ADC 串行数据接口时钟信号 output reg ADC_CS_N; //ADC 串行数据接口使能信号 input ADC_DOUT; //ADC转换结果,由ADC输给FPGA output reg ADC_DIN; //ADC控制信号输出,由FPGA发送通道控制字给ADC reg [2:0]r_Channel; //通道选择内部寄存器 reg [11:0]r_data; //转换结果读取内部寄存器 reg [7:0]DIV_CNT;//分频计数器 reg SCLK2X;//2倍SCLK的采样时钟 reg [5:0]SCLK_GEN_CNT;//SCLK生成暨序列机计数器 reg en;//转换使能信号 //在每个使能转换的时候,寄存Channel的值,防止在转换过程中该值发生变化 always@(posedge Clk or negedge Rst_n) if(!Rst_n) r_Channel <= 3'd0; else if(En_Conv) r_Channel <= Channel; else r_Channel <= r_Channel; //产生使能转换信号 always@(posedge Clk or negedge Rst_n) if(!Rst_n) en <= 1'b0; else if(En_Conv) en <= 1'b1; else if(Conv_Done) en <= 1'b0; else en <= en; //生成2倍SCLK使能时钟计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) DIV_CNT <= 8'd0; else if(en)begin if(DIV_CNT == (DIV_PARAM - 1'b1)) DIV_CNT <= 8'd0; else DIV_CNT <= DIV_CNT + 1'b1; end else DIV_CNT <= 8'd0; //生成2倍SCLK使能时钟 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK2X <= 1'b0; else if(en && (DIV_CNT == (DIV_PARAM - 1'b1))) SCLK2X <= 1'b1; else SCLK2X <= 1'b0; //生成序列计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK_GEN_CNT <= 6'd0; else if(SCLK2X && en)begin if(SCLK_GEN_CNT == 6'd33) SCLK_GEN_CNT <= 6'd0; else SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'd1; end else SCLK_GEN_CNT <= SCLK_GEN_CNT; //序列机实现ADC串行数据接口的数据发送和接收 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin ADC_SCLK <= 1'b1; ADC_CS_N <= 1'b1; ADC_DIN <= 1'b1; end else if(en) begin if(SCLK2X)begin case(SCLK_GEN_CNT) 6'd0:begin ADC_CS_N <= 1'b0; end 6'd1:begin ADC_SCLK <= 1'b0; ADC_DIN <= 1'b0; end 6'd2:begin ADC_SCLK <= 1'b1; end 6'd3:begin ADC_SCLK <= 1'b0; end 6'd4:begin ADC_SCLK <= 1'b1; end 6'd5:begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[2];end //addr[2] 6'd6:begin ADC_SCLK <= 1'b1; end 6'd7:begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[1];end //addr[1] 6'd8:begin ADC_SCLK <= 1'b1; end 6'd9:begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[0];end //addr[0] //每个上升沿,寄存ADC串行数据输出线上的转换结果 6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30,6'd32: begin ADC_SCLK <= 1'b1; r_data <= {r_data[10:0], ADC_DOUT}; end //循环移位寄存DOUT上的12个数据 6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,6'd27,6'd29,6'd31: begin ADC_SCLK <= 1'b0; end 6'd33:begin ADC_CS_N <= 1'b1; end default:begin ADC_CS_N <= 1'b1; end //将转换结果输出 endcase end else ; end else begin ADC_CS_N <= 1'b1; end //转换完成时,将转换结果输出到Data端口,同时产生一个时钟周期的高脉冲信号 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin Data <= 12'd0; Conv_Done <= 1'b0; end else if(en && SCLK2X && (SCLK_GEN_CNT == 6'd33))begin Data <= r_data; Conv_Done <= 1'b1; end else begin Data <= Data; Conv_Done <= 1'b0; end //产生ADC工作状态指示信号 assign ADC_State = ADC_CS_N; endmodule ADC工程 下载地址:https://wwek.lanzoub.com/iDiv00jdsyrc 提取码: 仿真结果 图片
FPGA&ASIC
# 小实验
刘航宇
4年前
0
1,820
2
【FPGA】深度理解串口接收模块设计与验证
本章导读 本章将学习 UART 的数据接收设计部分,针对实际使用中强电磁干扰可能会对数据的影响,本节提出一种改进型的串口接收模块设计方式。除了进行常规的功能仿真以外依旧使用 ISSP 工具进行板级测试。 目录 本章导读 串口接收原理分析 UART 异步串行通信接收模块设计与实现串口接收模块接口设计 这里输入数据相对于系统时钟是个异步信号,因此也需要对其进行同步,这里的处理方式与按键的输入部分一样,不再赘述。采样时钟生成模块设计 采样数据接收模块设计 数据状态判定模块设计 仿真及板级验证 代码工程 串口接收原理分析 上一节中学习了串口发送模块的设计与实现,其 UART 发送端发送一个字节数据时序图如图 17.1 所示。 图片 这一讲介绍串口接收模块的设计与实现。当对于数据线 Rs232_Rx 上的每一位进行采样时,一般情况下认为每一位数据的中间点是最稳定的。因此一般应用中,采集中间时刻时的电平即认为是此位数据的电平,如图 17.2 所示。 图片 但是在实际工业应用中,现场往往有非常强的电磁干扰,只采样一次就作为该数据的电平状态是不可靠的。很有可能恰好采集到被干扰的信号而导致结果出错,因此这里提出以下改进型的单 bit 数据接收方式示意图,使用多次采样求概率的方式进行状态判定,如图 17.3所示。 图片 图 17.3 改进型串口接收方式示意图在图 17.3 中,将每一位数据再平均分成了 16 小段。对于 Bit_x 这一位数据,考虑到数据在刚刚发生变化和即将发生变化的这一时期,数据极有可能不稳定的(用深灰色标出的两段),在这两个时间段采集数据,很有可能得到错误的结果,因此判定这两段时间的电平无效,采集时直接忽略。而中间这一时间段(用浅灰色标出),数据本身是比较稳定的,一般都代表了正确的结果。也就是前面提到的中间测量方式,但是也不排除该段数据受强电磁干扰而出现错误的电平脉冲。因此对这一段电平,进行多次采样,并求高低电平发生的概率,6 次采集结果中,取出现次数多的电平作为采样结果。例如,采样 6 次的结果分别为1/1/1/1/0/1/,则取电平结果为 1,若为 0/0/1/0/0/0,,则取电平结果为 0,当 6 次采样结果中 1和 0 各占一半(各 3 次),则可判断当前通信线路环境非常恶劣,数据不具有可靠性,不进 行处理。同理7次采集可以多数决定少数。 UART 异步串行通信接收模块设计与实现 串口接收模块接口设计 基于以上原理,串口接收模块整体框图如图 17.4 所示,其接口列表如表 17.1 所示。 图片 RS232 串行输入信号同步设计 这里输入数据相对于系统时钟是个异步信号,因此也需要对其进行同步,这里的处理方式与按键的输入部分一样,不再赘述。 reg s0_Rs232_Rx,s1_Rs232_Rx; //同步寄存器 reg tmp0_Rs232_Rx,tmp1_Rs232_Rx; //数据寄存器 //同步寄存器,消除亚稳态 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin s0_Rs232_Rx <= 1'b0; s1_Rs232_Rx <= 1'b0; end else begin s0_Rs232_Rx <= Rs232_Rx; s1_Rs232_Rx <= s0_Rs232_Rx; end //数据寄存器 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin tmp0_Rs232_Rx <= 1'b0; tmp1_Rs232_Rx <= 1'b0; end else begin tmp0_Rs232_Rx <= s1_Rs232_Rx; tmp1_Rs232_Rx <= tmp0_Rs232_Rx; end assign nedege = !tmp0_Rs232_Rx && tmp1_Rs232_Rx;采样时钟生成模块设计 串口接收模块主要构成之一即为波特率时钟生成模块。这里根据本章原理部分提到的过采样方式,即实际的采样频率是波特率的 16 倍,得出计数值与波特率之间的关系如表 17.2所示,其中系统时钟周期为 System_clk_period,这里为 20ns。 图片 这里依旧使用一个选择器,来实现不同波特率与采样时钟分频计数值之间的对应关系。设计代码如下所示。 reg [15:0]bps_DR; always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_DR <= 16'd324; else begin case(baud_set) 0:bps_DR <= 16'd324; 1:bps_DR <= 16'd162; 2:bps_DR <= 16'd80; 3:bps_DR <= 16'd53; 4:bps_DR <= 16'd26; default:bps_DR <= 16'd324; endcase end现在产生采样时钟,即波特率时钟的 16 倍。 reg [15:0]div_cnt; reg bps_clk; always@(posedge Clk or negedge Rst_n) if(!Rst_n) div_cnt <= 16'd0; else if(uart_state)begin if(div_cnt == bps_DR) div_cnt <= 16'd0; else div_cnt <= div_cnt + 1'b1; end else div_cnt <= 16'd0; always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_clk <= 1'b0; else if(div_cnt == 16'd1) bps_clk <= 1'b1; else bps_clk <= 1'b0;采样时钟计数器,计数器清零条件之一 bps_cnt == 8'd159 代表一个字节接收完毕。(bps_cnt == 8'd12 && (START_BIT > 2))是实现起始位检测是否出错,在后面会对此进行详细解释。 reg [7:0]bps_cnt; always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_cnt <= 8'd0; else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2))) bps_cnt <= 8'd0; else if(bps_clk) bps_cnt <= bps_cnt + 1'b1; else bps_cnt <= bps_cnt; always@(posedge Clk or negedge Rst_n) if(!Rst_n) Rx_Done <= 1'b0; else if(bps_cnt == 8'd159) Rx_Done <= 1'b1; else Rx_Done <= 1'b0;采样数据接收模块设计 以图 17.3 起始位为例,位于中间的采样时间段对应的 bps_cnt 值分别为 6、7、8、9、10、11,在这些时刻直接累加本位数据。然后,后一位数据的采样时间段的第一个 bps_cnt值为前一位数据采样时间段的第一个 bps_cnt 值加 16。所以,起始位后面紧跟的第一个数据位 Bit0 的采样时间段的 bps_cnt 值分别是 22、23、24、25、26、27,同样,在这些时刻,直接累加本位数据。以此类推,可以得到其他位的采样时间段对应的 bps_cnt 值。 现解释为何在上面清零条件之一为(bps_cnt == 8'd12 && (START_BIT > 2)),理想情况下(真正的起始位)也就是当 bps_cnt 计数值为 12 时,START_BIT 的计算值应该为 0。而在实际中,有可能会出现一个干扰信号,而并非真正的起始信号,也导致下降沿的出现。此时,如果不加判断,直接视之为起始信号,进入接收状态,那么必然会接收到错误数据,也可能导致错过正确数据的接收。为了增加抗干扰能力,这里采样了这样的一种思路:当数据线上出现了下降沿时,先假设它是起始信号,然后对其进行中间段采样。如果这 6 次采样值累加结果大于 2,即 6 次采样中有至少一半的状态为高电平时,那么这显然不符合真正起始信号的持续低电平要求,此时就把刚才到来的下降沿视为干扰信号,而不视为起始信号。 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin START_BIT <= 3'd0; r_data_byte[0] <= 3'd0; r_data_byte[1] <= 3'd0; r_data_byte[2] <= 3'd0; r_data_byte[3] <= 3'd0; r_data_byte[4] <= 3'd0; r_data_byte[5] <= 3'd0; r_data_byte[6] <= 3'd0; r_data_byte[7] <= 3'd0; STOP_BIT = 3'd0; end else if(bps_clk)begin case(bps_cnt) 0:begin START_BIT <= 3'd0; r_data_byte[0] <= 3'd0; r_data_byte[1] <= 3'd0; r_data_byte[2] <= 3'd0; r_data_byte[3] <= 3'd0; r_data_byte[4] <= 3'd0; r_data_byte[5] <= 3'd0; r_data_byte[6] <= 3'd0; r_data_byte[7] <= 3'd0; STOP_BIT <= 3'd0; end 6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx; 22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx; 38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx; 54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx; 70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx; 86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx; 102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx; 118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx; 134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx; 150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx; default: begin START_BIT <= START_BIT; r_data_byte[0] <= r_data_byte[0]; r_data_byte[1] <= r_data_byte[1]; r_data_byte[2] <= r_data_byte[2]; r_data_byte[3] <= r_data_byte[3]; r_data_byte[4] <= r_data_byte[4]; r_data_byte[5] <= r_data_byte[5]; r_data_byte[6] <= r_data_byte[6]; r_data_byte[7] <= r_data_byte[7]; STOP_BIT <= STOP_BIT; end endcase end数据状态判定模块设计 在原理部分介绍过,对一位数据需进行 6 次采样,然后取出现次数较多的数据作为采样结果,也就是说,6 次采样中出现次数多于 3 次的数据才能作为最终的有效数据。对此,可以用接收到数据 r_data_byte[n]结合数值比较器来判断,也可以直接令其等于当前位的最高位数据。以下面例子说明:当 r_data_byte[n]分别为二进制的 011B/010B/100B/101B时,这几个数据十进制格式分别为 3d/2d/4d/5d,可以发现大于等 4d 的为 100B/101B。当最高位是 1 即此时的数据累加值大于等于 4d,可以说明数据真实值为 1;当最高位是 0 即此时的数据累加值小于等于 3d,可以说明数据真实值为 0,因此只需判断最高位即可。 always@(posedge Clk or negedge Rst_n) if(!Rst_n) data_byte <= 8'd0; else if(bps_cnt == 8'd159)begin data_byte[0] <= r_data_byte[0][2]; data_byte[1] <= r_data_byte[1][2]; data_byte[2] <= r_data_byte[2][2]; data_byte[3] <= r_data_byte[3][2]; data_byte[4] <= r_data_byte[4][2]; data_byte[5] <= r_data_byte[5][2]; data_byte[6] <= r_data_byte[6][2]; data_byte[7] <= r_data_byte[7][2]; end仿真及板级验证 完成设计之后,对其进行功能仿真。在下面的 testbench 文件中,这里产生数据的激励输入使用上一章的发送数据模块的输出来实现,因此激励文件只需在上一章的激励文件中,修改端口信息、例化本模块以及将发送模块输出的 Rs232_Tx 连接到接收模块上的 Rs232_Rx即可。修改后的部分激励文件如下: wire Rs232_Tx; uart_byte_rx uart_byte_rx( .Clk(Clk), .Rst_n(Rst_n), .baud_set(baud_set), .Rs232_Rx(Rs232_Tx), .data_byte(data_byte_r), .Rx_Done(Rx_Done) ); uart_byte_tx uart_byte_tx( .Clk(Clk), .Rst_n(Rst_n), .data_byte(data_byte_t), .send_en(send_en), .baud_set(baud_set), .Rs232_Tx(Rs232_Tx), .Tx_Done(Tx_Done), .uart_state(uart_state) );设置好仿真脚本后进行功能仿真,可以看到如图 17.5 所示的波形文件,每当一个字节发送结束后,数据输出 data_byte_r 均会更新输出一次。下图中由于 Rs232_Rx 仅声明了但并未调用,因此无数据显示,可以直接删除。 图片 与上一章不同,这里使用 ISSP 的探针功能,对本次数据接收模块进行板级调试与验证。其主要配置如图 17.6 所示,并加入到工程中。 图片 新建一个顶层文件 uart_rx_top.v,这里例化数据接收模块以及 ISSP 工具。只有接收成功后才采集下一次数据,符合实际使用情况。 module uart_rx_top(Clk,Rst_n,Rs232_Rx); input Clk; input Rst_n; input Rs232_Rx; reg [7:0]data_rx_r; wire [7:0]data_rx; wire Rx_Done; uart_byte_rx uart_byte_rx( .Clk(Clk), .Rst_n(Rst_n), .baud_set(3'd0), .Rs232_Rx(Rs232_Rx), .data_byte(data_rx), .Rx_Done(Rx_Done) ); issp issp( .probe(data_rx_r) ); always@(posedge Clk or negedge Rst_n) if(!Rst_n) data_rx_r <= 8'd0; else if(Rx_Done) data_rx_r <= data_rx; else data_rx_r <= data_rx_r; endmodule分配引脚并全编译无误后下载工程到开发板中。在 Quartus Prime 中点击 Tools→InSystem Source and Probes Editor 启动 ISSP,手动选择下载器后,并将数据格式改为设计中的 hex 格式,持续触发模式。打开电脑上的串口助手,将主要参数设置为:波特率为 9600、无校验位、8 位数据位以及 1bit 停止位。 图片 图 17.8 ISSP 工具设置界面在串口助手上先后输入 aa、38 后在 ISSP 使用界面可以看到 Data 会随之对应变化。可知设计无误。 图片 本章学习了串口接收的相关原理,在设计过程中针对工业现场的强电磁干扰等问题,提出了一种基于权重的改进型数据接收方式。并在板级调试中使用了 ISSP 中的探针功能(probe)。 在本章实验的基础上,可以将接收到的数据在 4 位 LED 或者数码管上进行更直观的显示。 代码工程 串口接收 下载地址:https://wwek.lanzoub.com/iEZDG0j3flaj 提取码:
FPGA&ASIC
# 小实验
刘航宇
4年前
0
743
0
【FPGA】【SPI】线性序列机与串行接口 DAC 驱动设计与验证
概述:ADC和DAC是FPGA与外部信号的接口,从数据接口类型的角度划分,有低速的串行接口和高速的并行接口。FPGA经常用来采集中高频信号,因此使用并行ADC和DAC居多。并行接口包括两种数字编码方式:带符号数signed与无符号数unsigned。 DAC DA转化一般将数字信号“010101”转化为模拟信号去控制其它电子设备。 导读: 数模转换器即 D/A 转换器,或简称 DAC(Digital to Analog Conver),是指将数字信号转变为模拟信号的电子元件。 DAC 的内部电路构成无太大差异,一般按输出是电流还是电压、能否作乘法运算等进行分类。大多数 DAC 由电阻阵列和 n 个电流开关(或电压开关)构成,按数字输入值切换开关,产生比例于输入的电流(或电压) 。此外,也有为了改善精度而把恒流源放入器件内部的。 DAC 可分为电压型和电流型两大类,电压型 DAC 有权电阻网络、T 型电阻网络和树形开关网络等;电流型 DAC 有权电流型电阻网络和倒 T 型电阻网络等。 电压输出型(如 TLV5618) 。电压输出型 DAC 虽有直接从电阻阵列输出电压的,但一般采用内置输出放大器以低阻抗输出。直接输出电压的器件仅用于高阻抗负载,由于无输出放大器部分的延迟,故常作为高速 DAC 使用。 电流输出型(如 THS5661A ) 。电流输出型 DAC 很少直接利用电流输出,大多外接电流- 电压转换电路得到电压输出,后者有两种方法:一是只在输出引脚上接负载电阻而进行电流- 电压转换,二是外接运算放大器。 乘算型(如 AD7533) 。DAC 中有使用恒定基准电压的,也有在基准电压输入上加交流信号的,后者由于能得到数字输入和基准电压输入相乘的结果而输出,因而称为乘算型 DAC。 乘算型 DAC 一般不仅可以进行乘法运算,而且可以作为使输入信号数字化地衰减的衰减器及对输入信号进行调制的调制器使用。 一位 DAC。一位 DAC 与前述转换方式全然不同,它将数字值转换为脉冲宽度调制或频率调制的输出,然后用数字滤波器作平均化而得到一般的电压输出,用于音频等场合。 本章以 TLV5618 为例介绍 DAC 的工作原理及时序图解释,并用线性序列机(LSM)来描述时序图进而正确驱动此类设备。在 Quartus Pime 软件中,使用 ISSP 工具输入希望输出的电压值,控制 FPGA 进而操作 TLV5618 芯片输出对应的电压值。 任务 使用FPGA芯片控制DAC采集芯片,输出指定的电压值。 硬件部分 为了将FPGA输出的数字电压转换成模拟电压,使用到了数模转换芯片(简称DAC)TLV5618。进行设计前, 需要查看该芯片的数据手册 。 1.芯片功能图 本章使用的 DAC 芯片为 TLV5618,其芯片内部结构如图所示。TLV5618 是一个基于电压输出型的双通道 12 位单电源数模转换器,其由串行接口、一个速度和电源控制器、电阻网络、轨到轨输出缓冲器组成。 图片 TLV5618 使用 CMOS 电平兼容的三线制串行总线与各种处理器进行连接,接收控制器发送的 16 位的控制字,这 16 位的控制字被分为 2 个部分,包括 4 位的编程位,12 位的数据位。 2.端口功能表 图片 从功能图和功能表中我们可以看出,TLV5618有四个输入端口: 片选信号CS、数据串行输入端口DIN、模拟参考电压REF、数字时钟SCLK。 两个输出端分别为OUTA和OUTB,均为对应的模拟电压输出端。 3.时序图 当片选(CS)信号为低电平时,输入数据以最高有效位在前的方式被读入 16 位移位寄存器。在 SCLK 输入信号的下降沿,把数据移入寄存器 A、B。当片选(CS)信号进入上升沿时,再把数据送至 12 位 A/D 转换器。 图片 从时序图中我们可以看到使用该芯片时要注意这几个参数: tw(L):低电平最小宽度,25ns。 tw(H):高电平最小宽度,25ns。 tsu(D):数据最短建立时间。 th(D):数据最短保持时间。 tsu(CS-CK):片选信号下降沿到第一个时钟下降沿最短时间。 th(CSH):片选信号最短拉高时间。 TLV5618 的 16 位数据格式如下: 图片 其中,SPD 为速度控制位,PWR 为电源控制位。上电时,SPD 和 PWR 复位到 0(低速模式和正常工作)。 图片 R1 与 R0 所有可能的组合以及代表的含义如下所示。如果其中一个寄存器或者缓冲区被选择,那么 12 位数据将决定新的 DAC 输出电压值。 图片 这样针对 D[15:12]不同组合构成的典型操作如下: 1)设置 DAC A 输出,选择快速模式:写新的 DAC A 的值,更新 DAC A 输出。DAC A 的输出在 D0 后的时钟上升沿更新。 图片 2)设置 DAC B 输出,选择快速模式: 写新的 DAC B 的值到缓冲区,并且更新 DAC B 输出。DAC B 的输出在 D0 后的时钟上升沿更新。 图片 3) 设置 DAC A、DAC B 的值,选择低速模式:在写 DAC A 的数据 D0 后的时钟上升沿 DAC A 和 B 同时更新输出。 a.写 DAC B 的数据到缓冲区: 图片 b.写新的 DAC A 的值并且同时更新 DAC A 和 B: 图片 4) 设置掉电模式:×=不关心 图片 4.输出电压计算 图片 图片 由手册给出的公式知,输出电压与输入的编码值成正比,同时还要乘以一个系数REF,这个系数从芯片的REF引脚输入。我们打开并查看开发板的原理图:其中参考电压为由 LM4040 提供的 2.048V,与FPGA 采用三线制 SPI 通信。 图片 从图中知,我们用到了芯片LM4040-2.0给DAC供电,这个芯片工作时输出电压为4.028V(即精度为12位),故参数REF为4.028。 5.时钟频率与刷新率计算 图片 我们查阅手册后知道,使用该芯片时,时钟最大频率为20MHz,刷新率为时钟频率的1/16。而开发板提供的原始时钟为50MHz,因此可以采用四分频后得到12.5MHz的时钟频率。 线性序列机设计思想与接口时序设计 从图 24.3 中可以看出,该接口的时序是一个很有规律的序列,SCLK 信号什么时候该由变高,什么时候由高变低。DIN 信号什么时候该传输哪一位数据,都是可以根据时序参数唯一确定下来的。 这样就可以将该数据波形放到以时间为横轴的一个二维坐标系中,纵轴就是每个信号对应的状态: 图片 因此只需要在逻辑中使用一个计数器来计数,然后每个计数值时就相当于在 t 轴上对应了一个相应的时间点,那么在这个时间点上,各个信号需要进行什么操作,直接赋值即可。 经查阅手册可知器件工作频率SCLK最大为20MHz,这里定义其工作频率为12.5MHz。设置一个两倍于 SCLK 的采样时钟 SCLK2X,使用 50M 系统时钟二分频而来即 SCLK2X 为25MHz。针对 SCLK2X 进行计数来确定图 24.4 中各个信号的状态。可得出每个时间点对应信号操作详表。 图片 图片 线性序列机计数器的控制逻辑判断依据,如表 24.7 所示。 图片 以上就是通过线性序列机设计接口时序的一个典型案例,可以看到,线性序列机可以大大简化设计思路。线性序列机的设计思想就是使用一个计数器不断计数,由于每个计数值都会对应一个时间,那么当该时间符合需要操作信号的时刻时,就对该信号进行操作。这样,就能够轻松的设计出各种时序接口了。 基于线性序列机的 DAC 驱动设计 模块接口设计 设计 TLV5618 接口逻辑的模块如图 24.8 所示。 图片 其中,每个端口的功能描述如表 24.6 所示。 表 24.6 模块端口功能描述 图片 生成使能信号,当输入使能信号有效后便将使能信号 en 置 1,当转换完成信号有效时便将其重新置 0。 reg en;//转换使能信号 always@(posedge Clk or negedge Rst_n) if(!Rst_n) en <= 1'b0; else if(Start) en <= 1'b1; else if(Set_Done) en <= 1'b0; else en <= en;在数据手册中SCLK的频率范围为0.8~3.2MHz。这里为了方便适配不同的频率需求率,设置了一个可调的计数器,改变 DIV_PARAM 的值即可改变 DAC 工作频率。根据表 中可以看出,需要根据计数器的值周期性的产生 SCLK 时钟信号,这里可以将计数器的值等倍数放大,形成过采样。这里产生一个两倍于 SCLK 的信号,命名为 SCLK2X。 首先编写分频计数器,时钟 SCLK2X 的计数器。 //生成 2 倍 SCLK 使能时钟计数器 reg [7:0]DIV_CNT;//分频计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) DIV_CNT <= 4'd0; else if(en)begin if(DIV_CNT == (DIV_PARAM - 1'b1))//2-1=1,cnt=0,25MHZ,cnt=1为12.5MHZ DIV_CNT <= 4'd0; else DIV_CNT <= DIV_CNT + 1'b1; end else DIV_CNT <= 4'd0;根据使能信号以及计数器状态生成 SCLK2X 时钟。 //生成 2 倍 SCLK 使能时钟计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK2X <= 1'b0; else if(en && (DIV_CNT == (DIV_PARAM - 1'b1))) SCLK2X <= 1'b1; else SCLK2X <= 1'b0;每当使能转换后,对 SCLK2X 时钟进行计数。 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK_GEN_CNT <= 6'd0; else if(SCLK2X && en)begin if(SCLK_GEN_CNT == 6'd32) SCLK_GEN_CNT <= 6'd0; else SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'd1; end else SCLK_GEN_CNT <= SCLK_GEN_CNT;根据 SCLK2X 计数器的值来确认工作状态以及数据传输进程。 //依次将数据移出到 DAC 芯片 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin DIN <= 1'b1; SCLK <= 1'b0; r_DAC_DATA <= 16'd0; end else begin if(Start)//收到开始发送命令时,寄存 DAC_DATA 值 r_DAC_DATA <= DAC_DATA; if(!Set_Done && SCLK2X) begin if(!SCLK_GEN_CNT[0])begin //偶数, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30: SCLK <= 1'b1; DIN <= r_DAC_DATA[15]; r_DAC_DATA <= #1 r_DAC_DATA << 1; end else //奇数, 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31: SCLK <= 1'b0; end endDAC 工作状态,处于数据传输状态时 CS_N 为低电平状态,空闲时为高。 always@(posedge Clk or negedge Rst_n) if(!Rst_n) CS_N <= 1'b1; else if(en && SCLK2X) CS_N <= SCLK_GEN_CNT[5]; else CS_N <= CS_N;一次转换结束的标志,即 SCLK_GEN_CNT[5] && SCLK2X,并产生一个高脉冲的转换完成标志信号 Set_Done。 assign Set_Done = SCLK_GEN_CNT[5] && SCLK2X;仿真及板级测试 这里仿真文件需输出几次并行数据,观测串行数据输出 DIN 的状态即可判断是否能驱动正常。这里输出四次数据分别为 C_AAAh、4_555h、1_555h、F_555h。部分代码如下,只写出了前两个数据,后面两个可直接复制修改即可。 initial begin Rst_n = 0; Start = 0; DAC_DATA = 0; #201; Rst_n = 1; #200; DAC_DATA = 16'hC_AAA; Start = 1; #20; Start = 0; #200; wait(Set_Done); #20000; DAC_DATA = 16'h4_555; Start = 1; #20; Start = 0; #200; wait(Set_Done); $stop; end开始仿真后,可看出人为控制 DAC_DATA 数据输入状态正常。 图片 放大第一个数据传输过程,可以看出正常 1100_1010_1010_1010b,计数器计数到'd32 符合设计要求,且每个传输过程中 CS_N 为低。传输完成后产生一个时钟周期的 Set_Done 标志信号。 图片 为了再次验证 TLV5618 驱动模块设计的正确性,使用 ISSP 在线调试工具。创建一个 ISSPIP 核,主要配置如图 24.7 所示。 图片 加入工程后新建顶层文件 DAC_test.v,并对 ISSP 以及设计好的 TLV5618 进行例化. 在接口时序介绍中指出,TLV5618 有三种更新输出电压方式,下面分别测试这三种电压更新方式。上电复位后两通道输出电压初始值均为 0V。 1.单独测试 A 通道,依次输入 CFFFh、C7FFh、C1FFh,理论输出电压值应为 4.096、2.048、0.512。可在通道 A 测量输出电压依次为 4.10、2.05、0.51,此时通道 B 电压一直保持 0,电压输出在误差允许范围内。 2.单独测试 B 通道,依次输入 4FFFh、47FFh、4000h,可在通道 B 测量输出电压依次为4.10、2.05、0。此时通道 A 电压一直保持 0.51,电压输出在误差允许范围内。 3.测量 AB 两通道同时更新,首先输入 1FFF 将数据写入通道 B 寄存器,再写入 8FFF 到通道 A 寄存器。这样可以测量出写完后会两个通道输出电压会同时变为 4.10。 通过以上三组测试数据的,可以发现 DAC 芯片输出电压数据更新正常。 这样就完成了一个 DAC 模块的设计与仿真验证,基于本讲以及 14 讲即可实现信号发 生器,详细内容可以参考第五篇中的进阶课程 DDS2。 设计工程 图片 我们考虑用FPGA设计一个DAC驱动,通过CS、sclk、din三根信号线与DAC芯片连接,设计输入端口Data[15:0]。同时为了便于与其他模块共同协作,我们加上了使能端口en和转换完成标志位Conv_done,这是FPGA设计时必须考虑的一点,对于复杂的驱动模块,这两个信号是不可或缺的。 软件部分 //驱动部分 module tlv5618( Clk, Rst_n, DAC_DATA, //并行数据输入端 Start,//开始标志位 Set_Done,//完成标志位 DAC_CS_N,//片选 DAC_DIN,//串行数据送给ADC芯片 DAC_SCLK,//工作时钟SCLK DAC_State//工作状态 ); parameter fCLK=50;//50MHZ时钟参数 parameter DIV_PARAM=2;//分频参数 input Clk; input Rst_n; input[15:0] DAC_DATA; input Start; output reg Set_Done; output reg DAC_CS_N; output reg DAC_DIN; output reg DAC_SCLK; output DAC_State; assign DAC_State=DAC_CS_N;//工作状态标志与片选信号相同 reg [15:0] r_DAC_DATA;//DAC数据寄存器 reg[3:0] DIV_CNT;//分频计数器 reg SCLK2X;//2倍SCLK的采样时钟 reg[5:0] SCLK_GEN_CNT;//SCLK生成暨序列机计数器 reg en; wire trans_done;//转化序列完成标志信号 always @(posedge Clk or negedge Rst_n) begin if(!Rst_n) en<=1'b0; else if(Start) en<=1'b1; else if(trans_done) en<=1'b0;//转换完成后将使能关闭 else if(treans_done) en<=1'b0;//转换完成后将使能关闭 else en<en; end //分频计数器 always@(posedge Clk or negedge Rst_n)begin if(!Rst_n) DIV_CNT<=4'd0; else if(en)begin if(DIV_CNT==(DIV_PARAM-1'b1))//前面设置了分频系数为2,这里计数器能够容纳2拍时钟脉冲 DIV_CNT<=4'b0; else DIV_CNT<=DIV_CNT+1'b1; end else DIV_CNT<=4'd0; end //二分频 always@(posedge Clk or negedge Rst_n)begin if(!Rst_n) SCLK2X<=1'b0; else if(en && (DIV_CNT==(DIV_PARAM-1'b1))) SCLK2X<=1'b1; else SCLK2X<=1'b0; end //生成序列计数器,对SCLK脉冲进行计数 always@(posedge Clk or negedge Rst_n)begin if(!Rst_n) SCLK_GEN_CNT<=6'd0; else if(SCLK2X && en)begin//在高脉冲期间,累计拍数 if(SCLK_GEN_CNT==6'd33) SCLK_GEN_CNT<=6'd0; else SCLK_GEN_CNT<=SCLK_GEN_CNT+1'd1; end else SCLK_GEN_CNT<=SCLK_GEN_CNT; end always@(posedge Clk or negedge Rst_n)begin if(!Rst_n) r_DAC_DATA<=16'd0; else if(Start) //收到开始发送命令时候,寄存DAC_DATA值 r_DAC_DATA<=DAC_DATA; else r_DAC_DATA<=r_DAC_DATA; end //依次将数据移出到DAC芯片 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin DAC_DIN<=1'b1; DAC_SCLK<=1'b0; DAC_CS_N<=1'b1; end else if(!Set_Done && SCLK2X)begin case(SCLK_GEN_CNT) 0: begin //高脉冲期间内,计数为0时了,打开片选使能,给予时钟上升沿,将最高位数据送给DAC芯片 DAC_CS_N <= 1'b0; DAC_DIN <= r_DAC_DATA[15]; DAC_SCLK <= 1'b1; end 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31: begin DAC_SCLK <= 1'b0; //时钟低电平 end 2: begin DAC_DIN <= r_DAC_DATA[14]; DAC_SCLK <= 1'b1; end 4: begin DAC_DIN <= r_DAC_DATA[13]; DAC_SCLK <= 1'b1; end 6: begin DAC_DIN <= r_DAC_DATA[12]; DAC_SCLK <= 1'b1; end 8: begin DAC_DIN <= r_DAC_DATA[11]; DAC_SCLK <= 1'b1; end 10: begin DAC_DIN <= r_DAC_DATA[10]; DAC_SCLK <= 1'b1; end 12: begin DAC_DIN <= r_DAC_DATA[9]; DAC_SCLK <= 1'b1; end 14: begin DAC_DIN <= r_DAC_DATA[8]; DAC_SCLK <= 1'b1; end 16: begin DAC_DIN <= r_DAC_DATA[7]; DAC_SCLK <= 1'b1; end 18: begin DAC_DIN <= r_DAC_DATA[6]; DAC_SCLK <= 1'b1; end 20: begin DAC_DIN <= r_DAC_DATA[5]; DAC_SCLK <= 1'b1; end 22: begin DAC_DIN <= r_DAC_DATA[4]; DAC_SCLK <= 1'b1; end 24: begin DAC_DIN <= r_DAC_DATA[3]; DAC_SCLK <= 1'b1; end 26: begin DAC_DIN <= r_DAC_DATA[2]; DAC_SCLK <= 1'b1; end 28: begin DAC_DIN <= r_DAC_DATA[1]; DAC_SCLK <= 1'b1; end 30: begin DAC_DIN <= r_DAC_DATA[0]; DAC_SCLK <= 1'b1; end 32: DAC_SCLK <= 1'b1; //时钟拉高 33: DAC_CS_N <= 1'b1; //关闭片选 default:; endcase end assign trans_done = (SCLK_GEN_CNT == 33) && SCLK2X; always@(posedge Clk or negedge Rst_n) if(!Rst_n) Set_Done <= 1'b0; else if(trans_done) Set_Done <= 1'b1; else Set_Done <= 1'b0; endmodule//顶层模块 module DAC_test( Clk,//模块时钟50M Rst_n,//模块复位 DAC_CS_N, //TLV5618的CS_N接口 DAC_DIN, //TLV5618的DIN接口 DAC_SCLK //TLV5618的SCLK接口 ); input Clk; input Rst_n; output DAC_CS_N; output DAC_DIN; output DAC_SCLK; reg Start; reg [15:0]r_DAC_DATA; wire DAC_State; wire [15:0]DAC_DATA; wire Set_Done; tlv5618 tlv5618( .Clk(Clk), .Rst_n(Rst_n), .DAC_DATA(DAC_DATA), .Start(Start), .Set_Done(Set_Done), .DAC_CS_N(DAC_CS_N), .DAC_DIN(DAC_DIN), .DAC_SCLK(DAC_SCLK), .DAC_State(DAC_State) ); always@(posedge Clk or negedge Rst_n) if(!Rst_n) r_DAC_DATA <= 16'd0; else if(DAC_State) r_DAC_DATA <= DAC_DATA; always@(posedge Clk or negedge Rst_n) if(!Rst_n) Start <= 1'd0; else if(r_DAC_DATA != DAC_DATA) Start <= 1'b1; else Start <= 1'd0; endmodule`timescale 1ns/1ns module tlv5618_tb(); reg Clk; reg Rst_n; reg [15:0]DAC_DATA; reg Start; wire Set_Done; wire DAC_CS_N; wire DAC_DIN; wire DAC_SCLK; tlv5618 tlv5618( .Clk(Clk), .Rst_n(Rst_n), .DAC_DATA(DAC_DATA), .Start(Start), .Set_Done(Set_Done), .DAC_CS_N(DAC_CS_N), .DAC_DIN(DAC_DIN), .DAC_SCLK(DAC_SCLK), .DAC_State() ); initial Clk = 1; always#10 Clk = ~Clk; initial begin Rst_n = 0; Start = 0; DAC_DATA = 0; #201; Rst_n = 1; #200; DAC_DATA = 16'hC_AAA; Start = 1; #20; Start = 0; #200; wait(Set_Done); #20000; DAC_DATA = 16'h4_555; Start = 1; #20; Start = 0; #200; wait(Set_Done); #20000; DAC_DATA = 16'h1_555; Start = 1; #20; Start = 0; #200; wait(Set_Done); #20000; DAC_DATA = 16'hf_555; Start = 1; #20; Start = 0; #200; wait(Set_Done); #20000; $stop; end endmodule仿真 图片
FPGA&ASIC
# ASIC/FPGA
# 小实验
刘航宇
4年前
0
1,447
1
2022-11-30
VLSI设计-基4 Booth乘法器前端与中端实现
前言 在微处理器芯片中,乘法器是进行数字信号处理的核心,同时也是微处理器中进行数据处理的关键部件。乘法器完成一次操作的周期基本上决定了微处理器的主频。乘法器的速度和面积优化对于整个CPU的性能来说是非常重要的。为了加快乘法器的执行速度,减少乘法器的面积,有必要对乘法器的算法、结构及电路的具体实现做深入的研究。 目录 视频课程,关注本人B站账号有完整版前端中端设计教程 前言 视频课程,关注本人B站账号有完整版前端中端设计教程 1. 设计内容 2. 设计目标 3.原理介绍 4.电路&Verilog代码基4Booth编码器 4-2压缩器 超前进位加法器 4、仿真分析前端逻辑仿真 电路与性能仿真电路图 面积报告 功耗报告 后端 1. 设计内容 完成一个全定制的 8x8 bits 基-4 Booth 编码码乘法器核心电路设计,即可以不考虑输入、输出数据的寄存。 2. 设计目标 本设计最主要的目标是在电路速度尽可能高的条件下最小化电路的功率-延迟积(PDP)。所以首先在电路结构设计完成后需要分析、考虑最长延迟路径。根据设计目标进行逻辑链优化。 3.原理介绍 本乘法器采用基4booth编码,输入为两个8位有符号数,输出为16位有符号数。基4的booth编码将两个8位有符号数计算成4个部分积。4个部分积经过一层4-2压缩器得到2个部分积,得到两个部分积,两个部分积进过一个超前进位加法器(cla)得到最终结果。 图片 思维扩展: 若输入为两个128位有符号数,输出为256位有符号数。基4的booth编码将两个128位有符号数计算成64个部分积。64个部分积经过一层4-2压缩器得到32个部分积……在经过几层4-2压缩器,最终得到两个部分积,两个部分积进过一个超前进位加法器(cla)得到最终结果。结构框图如下: 图片 4.电路&Verilog代码 设计理念:功能需求->Verilog代码->代码转电路(DC)->电路转版图(ICC或SOCE) 基4Booth编码器 对于被乘数b_i进行编码,Booth 基-4 编码是根据相邻 3 位为一组,前后相邻分组重叠一比特位,从低位到高位逐次进行,在乘数的最右边另增加一位辅助位 0,作为分组的最低位。Booth 4-基编码的优点是可以减少 Booth 2-基产生部分积的一半,Booth 基-4 除了具有高速特性还具有低功耗的特点。 图片 对应case case(b_i) 3'b000 : booth_o <= 0; 3'b001 : booth_o <= { a_i[length-1], a_i}; 3'b010 : booth_o <= { a_i[length-1], a_i}; 3'b011 : booth_o <= a_i<<1; 3'b100 : booth_o <= -(a_i<<1); 3'b101 : booth_o <= -{a_i[length-1],a_i}; 3'b110 : booth_o <= -{a_i[length-1],a_i}; 3'b111 : booth_o <= 0; default: booth_o <= 0;4-2压缩器 4-2 压缩器的原理图如下所示,把 4 个相同权值的二进制数两个权值高一级的二进制数和,它有 5 个输入端口:包括 4 个待压缩数据 a1、a2、a3、a4 和一个初始进位或低权值 4-2 压缩传递的进位值 Ci;3 个输出端口:包括一比特位溢出进位值 Co,进位数据 C,伪和 S。 下面代码得到的结果out1的权值高一位,下一层部分积计算时需要将out1的结果左移一位(out1<<1); 图片 功能代码: assign w1 = in1 ^ in2 ^ in3 ^ in4; assign w2 = (in1 & in2) | (in3 & in4); assign w3 = (in1 | in2) & (in3 | in4); assign out2 = { w1[length*2-1] , w1} ^ {w3 , cin}; assign cout = w3[length*2-1]; assign out1 = ({ w1[length*2-1] , w1} & {w3 , cin}) | (( ~{w1[length*2-1] , w1}) & { w2[length*2-1] , w2});超前进位加法器 4位超前进位代码: //carry generator assign c[0] = cin; assign c[1] = g[0] + ( c[0] & p[0] ); assign c[2] = g[1] + ( (g[0] + ( c[0] & p[0]) ) & p[1] ); assign c[3] = g[2] + ( (g[1] + ( (g[0] + (c[0] & p[0]) ) & p[1])) & p[2] ); assign c[4] = g[3] + ( (g[2] + ( (g[1] + ( (g[0] + (c[0] & p[0]) ) & p[1])) & p[2] )) & p[3]); assign cout = c[width];代码下载 基4 Booth代码 下载地址:https://wwek.lanzoub.com/ikPEB0m2z41a 提取码: 4、仿真分析 前端逻辑仿真 本设计是单纯的组合逻辑,由仿真结果可知有符号乘法设计结果完全正确。 图片 电路与性能仿真 电路图 图片 面积报告 DC综合后,总共的单元面积为7853.630484等效门,总面积为78273.983938等效门。 图片 功耗报告 图片 由于该电路是完全的组合逻辑,无CLK端口,因此未作时序约束。 后端 做到这里完成了前端中端设计任务,在流片前还需要完成后端设计及验证。由于本电路规模大,我们可以利用EDA如(SOCE或者ICC)完成版图布局,由于时间仓促,笔者暂未更新后端教程。
FPGA&ASIC
VLSI&IC验证
# ASIC/FPGA
刘航宇
4年前
0
1,436
4
基本微带贴片天线设计理论与尺寸算法
理论 图片 贴片形式 图片 馈电方式 图片 尺寸推导 图片 图片 尺寸算法 %微带天线尺寸算法 c=3e8; f=0.915e9 %频率915MHZ er=2.2;%板材介电常数 h=1.575e-3;%板材厚度 w=c/(2*f)*sqrt(2/(er+1)) ereff=(er+1)/2+(er-1)/2*(1+12*h/w)^(-0.5); dL=(0.412*(ereff+0.3)*(w/h+0.264)*h)/((ereff-0.258)*(w/h+0.8)); Leff=c/(2*f*sqrt(ereff)); L=Leff-2*dL erl=(er+1)/2+(er-1)/2*(1+12*h/L)^(-0.5); Xf=L/2-L/(2*sqrt(erl)) Lgnd=L+6*h; Wgnd=w+6*h;微带线馈电 图片 1/4波长变换器 图片 计算推导 图片 尺寸算法 %微带天线尺寸算法 clear all; c=3e8; f=0.915e9 %频率915MHZ er=2.2;%板材介电常数 h=1.575e-3;%板材厚度 w=c/(2*f)*sqrt(2/(er+1)) ereff=(er+1)/2+(er-1)/2*(1+12*h/w)^(-0.5); dL=(0.412*(ereff+0.3)*(w/h+0.264)*h)/((ereff-0.258)*(w/h+0.8)); Leff=c/(2*f*sqrt(ereff)); L=Leff-2*dL erl=(er+1)/2+(er-1)/2*(1+12*h/L)^(-0.5); Xf=L/2-L/(2*sqrt(erl)) Lgnd=L+6*h; Wgnd=w+6*h; %Microstrip Line feed lambda_0=c/f; if w<=lambda_0 G=w^2/(90*lambda_0^2); else G=w^2/(120*lambda_0^2); end Yin=2*G; Rin=1/Yin ZT0=sqrt(Rin*50)
通信&信息处理
# 天线设计
刘航宇
4年前
1
3,453
11
2022-11-27
【天线/射频】什么是回波损耗?什么是插入损耗?
一、前言 什么是回波损耗?什么又是插入损耗? 这个貌似很容易回答,回波损耗吗,就是Return Loss,缩写为RL,S11,插入损耗就是 Insertion Loss,IL,S21。确实没错,就是这么简单。但是为什么叫做回波呢?为什么又叫做插入呢?今天我们仔细掰扯掰扯。 二、回波损耗 回波损耗,又称为反射损耗。( 越大越好 )是电缆链路由于阻抗不匹配所产生的反射,是一对线自身的反射。不匹配主要发生在连接器的地方,但也可能发生于电缆中特性阻抗发生变化的地方,所以施工的质量是提高回波损耗的关键。回波损耗将引入信号的波动,返回的信号将被双工的千兆网误认为是收到的信号而产生混乱。 回波损耗:return loss。回波损耗是表示信号反射性能的参数。回波损耗说明入射功率的一部分被反射回到信号源。例如,如果注入1mW (0dBm)功率给放大器其中10%被反射(反弹)回来,回波损耗就是-10dB。从数学角度看,回波损耗为-10 lg [(反射功率)/(入射功率)]。回波损耗通常在输入和输出都进行规定。 三、插入损耗 插入损耗:insertion loss。( 越小越好 )指在传输系 统的某处由于元件或器件的插入而发生的负载功率的损耗,它表示为该元件或器件插入前负载上所接收到的功率与插入后同一负载上所接收到的功率以分贝为单位的比值。 1.插入损耗是指发射机与接收机之间,插入电缆或元件产生的信号损耗,通常指衰减。插入损耗以接收信号电平的对应分贝(dB)来表示。 2.插入损耗多指功率方面的损失,衰减是指信号电压的幅度相对测量插入损耗的电路原信号幅度的变小。 通道的插入损耗是指输出端口的输出光功率与输入端口输入光功率之比,以dB为单位。插入损耗与输入波长有关,也与开关状态有关。定义为:IL=-10log(Po/Pi) 四、插入损耗和回波损耗系统介绍 图片 首先,我们拿到一个系统,就是上面这个黑盒子,当电磁波信号进入这个黑盒子时,我们通常不需要去关心电磁波在里面经历了什么。我们只要知道它出来是什么就行了。但是,当电磁波从外面进入这个黑盒子时,相当于从一个介质进入到另一个介质,在交界面上会有一部分电磁波被挡在外面,这部分被挡在外面的电磁波就被反射回去了,返回去的电磁波就是回波。而进去的这部分电磁波在通过这个黑盒子时,就必然会被这个黑家伙影响到,如果这家伙有一点点贪心,肚子里装了一点点的小电阻材料,那么就留一点点买路钱就够了,就去的电磁波在交完买路钱之后,大部分会传输出去,这个被劫走的一部分电磁波能量我们就认为被损失了,无论是回波还是黑盒子吃掉的都是电磁波能量时导致的能量的损耗,都是由于黑盒子插入进来导致的损耗,所以通常被称为插入损耗。如下图所示。 图片 所以我们知道,其实一个微波元器件的插入损耗是由两部分组成的:回波损耗和电阻损耗。 回波损耗的这部分电磁波能量被端口的不匹配给反射回去了,并没有转化成热散出去,所以我们在射频系统设计和分析中,要提防着这部分回波倒灌会对系统带来的影响。通常这部分倒灌的能量不是很大,怎么计算呢? 举个简单的例子,比如上图端口1输入的电磁波功率是1W,即30dBm,如果端口1 的回波损耗RL=20dB。那么反射回去的回波的功率就是:30dBm-20dB=10dBm,也就是有10mW的功率被反射回去了。如果端口1 输入的功率是1kW,即60dBm,那么反射回去的回波功率就有10W了。如果端口1的回波损耗只有10dB,那将会有100W的功率被反射回去。这也证明了匹配的端口阻抗匹配的重要性。还有就是,功率越大,我们系统所要求的回波损耗就越小,在一些小功率的产品中,比如手机,匹匹配有个10dB就足够了。但是在电视发射系统中,因为发射机的功率通常会有上千瓦,所以,回波损耗通常要求在26dB以下。 注意,插入损耗的部分损耗也是有回波导致的啊,所以回波损耗越差,插入损耗就越大。 在端口匹配良好的系统中,插入损耗主要是由器件的电阻性材料导致的,这些电阻性材料导致的损耗,会转化成热。就像我们在衰减器中介绍的一样。其实转化成热,有时候更让我们头疼。因为热这个东西太可怕了,几乎所有的电子器件都有一个可以正常工作的温度范围,当超过这个温度了之后,工作就不正常了。长期在高温环境下工作,器件老化速率会加快,更容易损坏。
通信&信息处理
# 天线设计
刘航宇
4年前
1
4,057
1
关于射频芯片马上懂!
一部可支持打电话、发短信、网络服务、APP应用的手机,通常包含五个部分:射频、基带、电源管理、外设、软件。 射频: 一般是信息发送和接收的部分; 基带: 一般是信息处理的部分; 电源管理: 一般是节电的部分,由于手机是能源有限的设备,所以电源管理十分重要; 外设: 一般包括LCD,键盘,机壳等; 软件: 一般包括系统、驱动、中间件、应用。 目录 射频芯片和基带芯片的关系 工作原理与电路分析 接收电路的结构和工作原理1.电路结构 2.各元件的功能与作用 3.接收信号流程 1.电路结构 2.各元件的功能与作用 3.发射信号流程 国产射频芯片产业链现状 在手机终端中,最重要的核心就是射频芯片和基带芯片。射频芯片负责射频收发、频率合成、功率放大;基带芯片负责信号处理和协议处理。那么射频芯片和基带芯片是什么关系? 射频芯片和基带芯片的关系 射频(Radio Frenquency)和基带(Base Band)皆来自英文直译。其中射频最早的应用就是Radio——无线广播(FM/AM),迄今为止这仍是射频技术乃至无线电领域最经典的应用。 基带则是band中心点在0Hz的信号,所以基带就是最基础的信号。有人也把基带叫做“未调制信号”,曾经这个概念是对的,例如AM为调制信号(无需调制,接收后即可通过发声元器件读取内容)。 但对于现代通信领域而言,基带信号通常都是指经过数字调制的,频谱中心点在0Hz的信号。而且没有明确的概念表明基带必须是模拟或者数字的,这完全看具体的实现机制。 言归正传,基带芯片可以认为是包括调制解调器,但不止于调制解调器,还包括信道编解码、信源编解码,以及一些信令处理。而射频芯片,则可看做是最简单的基带调制信号的上变频和下变频。 所谓调制,就是把需要传输的信号,通过一定的规则调制到载波上面让后通过无线收发器(RF Transceiver)发送出去的工程,解调就是相反的过程。 工作原理与电路分析 射频简称RF射频就是射频电流,是一种高频交流变化电磁波,为是Radio Frequency的缩写,表示可以辐射到空间的电磁频率,频率范围在300KHz~300GHz之间。每秒变化小于1000次的交流电称为低频电流,大于10000次的称为高频电流,而射频就是这样一种高频电流。高频(大于10K);射频(300K-300G)是高频的较高频段;微波频段(300M-300G)又是射频的较高频段。射频技术在无线通信领域中被广泛使用,有线电视系统就是采用射频传输方式。 射频芯片指的就是将无线电信号通信转换成一定的无线电信号波形, 并通过天线谐振发送出去的一个电子元器件,它包括功率放大器、低噪声放大器和天线开关。射频芯片架构包括接收通道和发射通道两大部分。 射频电路方框图图片 接收电路的结构和工作原理 接收时,天线把基站发送来电磁波转为微弱交流电流信号经滤波,高频放大后,送入中频内进行解调,得到接收基带信息(RXI-P、RXI-N、RXQ-P、RXQ-N);送到逻辑音频电路进一步处理。 该电路掌握重点:1、接收电路结构;2、各元件的功能与作用;3、接收信号流程。 1.电路结构 接收电路由天线、天线开关、滤波器、高放管(低噪声放大器)、中频集成块(接收解调器)等电路组成。早期手机有一级、二级混频电路,其目的把接收频率降低后再解调(如下图) 接收电路方框图图片 2.各元件的功能与作用 1)、手机天线: 结构:(如下图) 由手机天线分外置和内置天线两种;由天线座、螺线管、塑料封套组成。 图片 作用:a)、接收时把基站发送来电磁波转为微弱交流电流信号。b)、发射时把功放放大后的交流电流转化为电磁波信号。 2)、天线开关: 结构:(如下图) 手机天线开关(合路器、双工滤波器)由四个电子开关构成。 图片 作用: 完成接收和发射切换; 完成900M/1800M信号接收切换。 逻辑电路根据手机工作状态分别送出控制信号(GSM-RX-EN;DCS- RX-EN;GSM-TX-EN;DCS- TX-EN),令各自通路导通,使接收和发射信号各走其道,互不干扰。 由于手机工作时接收和发射不能同时在一个时隙工作(即接收时不发射,发射时不接收)。因此后期新型手机把接收通路的两开关去掉,只留两个发射转换开关;接收切换任务交由高放管完成。 3)、滤波器: 结构:手机中有高频滤波器、中频滤波器。 作用:滤除其他无用信号,得到纯正接收信号。后期新型手机都为零中频手机;因此,手机中再没有中频滤波器。 4)、高放管(高频放大管、低噪声放大器): 结构:手机中高放管有两个:900M高放管、1800M高放管。都是三极管共发射极放大电路;后期新型手机把高放管集成在中频内部。 高频放大管供电图图片 作用: 对天线感应到微弱电流进行放大,满足后级电路对信号幅度的需求。 完成900M/1800M接收信号切换。 原理: 供电:900M/1800M两个高放管的基极偏压共用一路,由中频同时路提供;而两管的集电极的偏压由中频CPU根据手机的接收状态命令中频分两路送出;其目的完成900M/1800M接收信号切换。 经过滤波器滤除其他杂波得到纯正935M-960M的接收信号由电容器耦合后送入相应的高放管放大后经电容器耦合送入中频进行后一级处理。 5)、中频(射频接囗、射频信号处理器): 结构:由接收解调器、发射调制器、发射鉴相器等电路组成;新型手机还把高放管、频率合成、26M振荡及分频电路也集成在内部(如下图)。 图片 作用: a)、内部高放管把天线感应到微弱电流进行放大; b)、接收时把935M-960M(GSM)的接收载频信号(带对方信息)与本振信号(不带信息)进行解调,得到67.707KHZ的接收基带信息; c)、发射时把逻辑电路处理过的发射信息与本振信号调制成发射中频; d)、结合13M/26M晶体产生13M时钟(参考时钟电路); e)、根据CPU送来参考信号,产生符合手机工作信道的本振信号。 3.接收信号流程 手机接收时,天线把基站发送来电磁波转为微弱交流电流信号,经过天线开关接收通路,送高频滤波器滤除其它无用杂波,得到纯正935M-960M(GSM)的接收信号,由电容器耦合送入中频内部相应的高放管放大后,送入解调器与本振信号(不带信息)进行解调,得到67.707KHZ的接收基带信息(RXI-P、RXI-N、RXQ-P、RXQ-N);送到逻辑音频电路进一步处理。 #发射电路的结构和工作原理 发射时,把逻辑电路处理过的发射基带信息调制成的发射中频,用TX-VCO把发射中频信号频率上变为890M-915M(GSM)的频率信号。经功放放大后由天线转为电磁波辐射出去。 该电路掌握重点:(1)、电路结构;(2)、各元件的功能与作用;(3)、发射信号流程。 1.电路结构 发射电路由中频内部的发射调制器、发射鉴相器;发射压控振荡器(TX-VCO)、功率放大器(功放)、功率控制器(功控)、发射互感器等电路组成。(如下图) 发射电路方框图图片 2.各元件的功能与作用 1)、发射调制器: 结构:发射调制器在中频内部,相当于宽带网络中的MOD。 作用:发射时把逻辑电路处理过的发射基带信息(TXI-P;TXI-N;TXQ-P;TXQ-N)与本振信号调制成发射中频。 2)、发射压控振荡器(TX-VCO): 结构:发射压控振荡器是由电压控制输出频率的电容三点式振荡电路;在生产制造时集成为一小电路板上,引出五个脚:供电脚、接地脚、输出脚、控制脚、900M/1800M频段切换脚。当有合适工作电压后便振荡产生相应频率信号。 作用:把中频内调制器调制成的发射中频信号转为基站能接收的890M-915M(GSM)的频率信号。 原理:众所周知,基站只能接收890M-915M(GSM)的频率信号,而中频调制器调制的中频信号(如三星发射中频信号135M)基站不能接收的,因此,要用TX-VCO把发射中频信号频率上变为890M-915M(GSM)的频率信号。 当发射时,电源部分送出3VTX电压使TX-VCO工作,产生890M-915M(GSM)的频率信号分两路走:a)、取样送回中频内部,与本振信号混频产生一个与发射中频相等的发射鉴频信号,送入鉴相器中与发射中频进行较;若TX-VCO振荡出频率不符合手机的工作信道,则鉴相器会产生1-4V跳变电压(带有交流发射信息的直流电压)去控制TX-VCO内部变容二极管的电容量,达到调整频率准确性目的。b)、送入功放经放大后由天线转为电磁波辐射出去。 从上看出:由TX-VCO产生频率到取样送回中频内部,再产生电压去控制TX-VCO工作;刚好形成一个闭合环路,且是控制频率相位的,因此该电路也称发射锁相环电路。 3)、功率放大器(功放): 结构:目前手机的功放为双频功放(900M功放和1800M功放集成一体),分黑胶功放和铁壳功放两种;不同型号功放不能互换。 作用:把TX-VCO振荡出频率信号放大,获得足够功率电流,经天线转化为电磁波辐射出去。 值得注意:功放放大的是发射频率信号的幅值,不能放大他的频率。 功率放大器的工作条件: a)、工作电压(VCC):手机功放供电由电池直接提供(3.6V); b)、接地端(GND):使电流形成回路; c)、双频功换信号(BANDSEL):控制功放工作于900M或工作于1800M; d)、功率控制信号(PAC):控制功放的放大量(工作电流); e)、输入信号(IN);输出信号(OUT)。 4)、发射互感器: 结构:两个线径和匝数相等的线圈相互靠近,利用互感原理组成。 作用:把功放发射功率电流取样送入功控。 原理:当发射时功放发射功率电流经过发射互感器时,在其次级感生与功率电流同样大小的电流,经检波(高频整流)后并送入功控。 5)、功率等级信号: 所谓功率等级就是工程师们在手机编程时把接收信号分为八个等级,每个接收等级对应一级发射功率(如下表),手机在工作时,CPU根据接的信号强度来判断手机与基站距离远近,送出适当的发射等级信号,从而来决定功放的放大量(即接收强时,发射就弱)。 附功率等级表: 图片 6)、功率控制器(功控): 结构:为一个运算比较放大器。 作用:把发射功率电流取样信号和功率等级信号进行比较,得到一个合适电压信号去控制功放的放大量。 原理:当发射时功率电流经过发射互感器时,在其次级感生的电流,经检波(高频整流)后并送入功控;同时编程时预设功率等级信号也送入功控;两个信号在内部比较后产生一个电压信号去控制功放的放大量,使功放工作电流适中,既省电又能长功放使用寿命(功控电压高,功放功率就大)。 3.发射信号流程 当发射时,逻辑电路处理过的发射基带信息(TXI-P;TXI-N;TXQ-P;TXQ-N),送入中频内部的发射调制器,与本振信号调制成发射中频。而中频信号基站不能接收的,要用TX-VCO把发射中频信号频率上升为890M-915M(GSM)的频率信号基站才能接收。当TX-VCO工作后,产生890M-915M(GSM)的频率信号分两路走: a)、一路取样送回中频内部,与本振信号混频产生一个与发射中频相等的发射鉴频信号,送入鉴相器中与发射中频进行较;若TX-VCO振荡出频率不符合手机的工作信道,则鉴相器会产生一个1-4V跳变电压去控制TX-VCO内部变容二极管的电容量,达到调整频率目的。 b)、二路送入功放经放大后由天线转化为电磁波辐射出去。为了控制功放放大量,当发射时功率电流经过发射互感器时,在其次级感生的电流,经检波(高频整流)后并送入功控;同时编程时预设功率等级信号也送入功控;两个信号在内部比较后产生一个电压信号去控制功放的放大量,使功放工作电流适中,既省电又能长功放使用寿命。 国产射频芯片产业链现状 在射频芯片领域,市场主要被海外巨头所垄断,国内射频芯片方面,没有公司能够独立支撑IDM的运营模式,主要为Fabless设计类公司;国内企业通过设计、代工、封装环节的协同,形成了“软IDM“”的运营模式。 图片 射频芯片设计方面,国内公司在5G芯片已经有所成绩,具有一定的出货能力。射频芯片设计具有较高的门槛,具备射频开发经验后,可以加速后续高级品类射频芯片的开发。 射频芯片封装方面,5G射频芯片一方面频率升高导致电路中连接线的对电路性能影响更大,封装时需要减小信号连接线的长度;另一方面需要把功率放大器、低噪声放大器、开关和滤波器封装成为一个模块,一方面减小体积另一方面方便下游终端厂商使用。为了减小射频参数的寄生需要采用Flip-Chip、Fan-In和Fan-Out封装技术。 Flip-Chip和Fan-In、Fan-Out工艺封装时,不需要通过金丝键合线进行信号连接,减少了由于金丝键合线带来的寄生电效应,提高芯片射频性能;到5G时代,高性能的Flip-Chip/Fan-In/Fan-Out结合Sip封装技术会是未来封装的趋势。 在射频芯片领域,市场主要被海外巨头所垄断,国内射频芯片方面,没有公司能够独立支撑IDM的运营模式,主要为Fabless设计类公司;国内企业通过设计、代工、封装环节的协同,形成了“软IDM“”的运营模式。 图片 Flip-Chip/Fan-In/Fan-Out和Sip封装属于高级封装,其盈利能力远高于传统封装。国内上市公司,形成了完整的FlipChip+Sip技术的封装能力。
VLSI&IC验证
# 射频IC
刘航宇
4年前
0
1,477
0
2022-10-11
读懂史密斯圆图
史密斯圆图能干啥用? 史密斯圆图,就是做高频电路之间的阻抗匹配用的。所谓阻抗是电路对电的阻碍能力,它是个矢量,也就是说阻抗值是个复数。这里面既包括实部电阻成分一即与电力 "顶牛儿”的那部分阻力;也包括虚部电抗成分一就是把电力拉偏的那部分阻力。两者加一块,就叫阻抗。阻抗匹配,是电路之间连接的一-个基本要求,简单讲就是输入阻抗和输出阻抗大致相当,方向相反。如果阻抗不匹配呢?轻者电路工作效率低,重者.工作异常或直接烧毁。具体到高频电路来说,这些危害不仅都有,而且比数字电路、低频模拟电路上的危害要严重得多。那高频电路的阻抗匹配是啥标准呢?就是让输入端电阻和输出端电阻是纯阻性,而且最好等于509或752 。那原来阻抗不匹配,现在咋弄它就匹配了呢?就是在两个电路之间,接上电容、电感、传输线,微带线、变压器之类的东西。把阻抗值矫正到阻抗相等面来。史密斯圆图的主要用途,一是计算接到电路里的那些电容器、电感器的参数值的。二是用来显示某一电路的频率一阻抗特性的。 史密斯圆图怎么用? 史密斯圆图,它相当于一个地图,它上面每一个点, 都代表一个复数形式的阻抗值,而其圆心叫做匹配点,它代表了实部50om,虚部0om的理想阻抗,那就是我们要到达的地方。做阻抗匹配,就是规划一条从阻抗点走到匹配点的线路。然后呢?然后就是利用这些点位之间的差值做计算了。这个事儿我就不讲了。因为过去用纸本史密斯圆图才需要自己算,现在有很多专用软件能替我们做,不仅能算刚才我提到的那个匹配元件的电容量、电感量,还能算出谐振电路的Q值,驻波比,衰减量等等,这就用不着咱们算了,诸位知道这些概念是啥意思,就行了!至于具体怎么用史密斯圆图软件?怎么看这些数据?怎么评价匹配的效果?别着急,我们后边再聊。 史密斯圆图第二节史密斯圆图该咋看? 简言之,是看一线、两弧和两圆。 我们说过,史密斯圆图上的每个点,都有实部值和虚部值,那么图上的线,其实也只有两种,一是等实部线,二是等虚部线。读史密斯圆图,就是先沿等虚部线,找到实部值,再沿着等实部线找到虚部值。那么一线和两弧,都是等虚部线;两圆,是等实部线!接下来我们就从一线讲起,说说史密斯圆图的刻度划分刻度值 线一一根特殊的等虚部线 图片 史密斯圆图被一条名为电阻线的蓝色横线分成 上下两个半区,上半部分叫电感区, 那里所有点的虛部值都为正;下半部分叫电容区,那里所有点的虚部值都为负。而电阻线本身的虚部阻抗值不正不负,他上面每一个点的阻抗值均为0欧。所以电阻线是一根特殊的虚部线,它是我们查找实部值的一把尺子。 电阻线上有三个点,最左侧的叫短路点,他表示实部值0欧姆,虚部值也为0欧姆的情况;最右侧的叫断路点,他表示实部值无穷大,虚部值也为0欧姆的情况;而中间点,也就是圆心那是匹配点,那里的阻值是“标准阻值”,一般情况下他是50欧姆。“三点” 是史密斯圆图的基点,也是我们校正天线分析仪的起点,一定记住。 归一化处理 但是请注意!虽然电阻线是一把测量实部阻值的尺子,但是这把尺子上标的不一定是实部阻值。纸本的史密斯圆图标注的是阻抗值/标准阻值的比值,这叫归-化处理,这个值叫归-化阻抗值。下图中的小红字标的是阻抗值,而大红字标的是归一化阻抗值9。咱们先说图.上的大红字,那就是归一化之后的阻抗值。如果我们是在纸本史密斯圆图上标图,那第一步应该把阻抗值转换成归一化阻抗值。 图片 以标准阻抗50欧姆为例。我们要找100欧姆的实部值,就去电阻线上找归一化阻抗值为100欧姆/50欧姆=2的点,而图上显示"0.2" 归一化阻抗值的地方,其实际阻抗值就是0.2*50欧姆= 10欧姆,而匹配点的归一化阻抗值永远为1,这是归一化阻抗标注法的一个识别标志。为什么要用归一化阻抗值标注刻度呢?因为很多时候,我们不是要把电路匹配到50欧姆上,而是要匹配到75欧姆或者300欧姆等阻抗上。 请看下图,这是75欧姆标准电阻时,史密斯原图上的情况,其中电阻线下方写的红字是阻抗值。电阻线上方的写的红字是归一化阻抗值。 图片 第二套刻度一一导纳值和归一化导纳值 等电阻线是一把度量实部值的尺子,它有阻抗值和归一化阻抗值和两种标注方法,其实在电脑版史密斯圆图的等电阻线上,还有一套刻度。就是用大绿字标注的归一化导纳值和用小绿字标注的导纳值。 有人想问导纳值是啥?导是电阻值的倒数,纳是电抗值的倒数,导纳加一块就是导纳值,它是阻抗值的倒数,导纳值的单位是西门子,写作“S",1S等于= 1000mS。给导纳值做归一化,也就是 将导纳值除以0.02S标准导纳值(0.02S, 其实就是1/50欧姆)。这两种标法的值,是等价的。 图片 例如,电阻线上25Ω那个点,它的导纳值为1/25Ω=0.04S,它的归一化导纳值就是0.04S/0.02S为=2。 导纳值既然和阻抗值完全等价,用法也一样,那我们要这个玩意做啥呢?简单讲,我们在标注初始阻抗点时,要根据红色的阻抗值坐标系做,而在做阻抗匹配时,则要用到绿色的导纳值坐标系。 两圆两弧---等实部圆和等虚部弧 图片 两圆---两种等实部线 讲特殊的电阻线--也就是0欧姆等虚部线之后,我们看看与电阻线相切的那些圆形。这些圆圈都是等实部线,那上面每一条线的实部值都相等。其中红色的圆圈都叫阻抗圆,而绿色的圆圈都叫导纳圆,我们在电阻线.上找到实部阻抗值或者实部导纳值之后,就要沿着阻抗圆或者电纳圆去找虚部 值。其中电阻线以上的点是正值,代表阻抗点的虚部值呈现感性,电阻线以下的点是负值,代表阻 抗点的虚部值呈现容性。 和看电阻线的规矩一-样,我们在标注初始阻抗值时只看红色的。 两弧---两种等虚部线 那么虚部值到那去查?从断路点发出,向史密斯圆图边界放射的红线叫等电抗弧;而从短路点发出,向史密斯圆图边界反射的绿线都叫等电纳弧9。这些都是等虚部线,我们找到实部值后,就是沿着实部线9到找到特定值的等虚部线,那虚部线的值标哪儿了呢,在史密斯圆图的外边界上。和刚才-个规矩,我们标初始阻抗值时是根据红色数字做的。总结一下,到底怎么读图? 总结一下,到底怎么读图? 第一步,用阻抗值除以标准电阻,得到归一化实部阻抗值和归一化虚部阻抗值。 第二步,在等电阻线上,找到归一化实部阻抗值对应的阻抗圆。 第三步,沿着阻抗圆,向上或向下旋转,找与归一化虚部阻抗值对应的等电抗弧。 第四步,做个记号,就算OK。 举个栗子~! 1002-j50Ω滴点在哪儿? 第一步,计算归一化实部阻抗值为100Ω/50Ω=2;归一化虚部阻抗值为-50Ω/50Ω=-1。 第二步,在等电阻线上,找到归一化阻抗值为2的阻抗圆。 第三步,沿着归一化阻抗值为2的阻抗圆下旋,转到-1 那个电抗弧上。 第四步,做个记号!我标的是X。 图片 那如果图.上标的是阻抗值呢? 如果是在电脑上识图,图上直接标注的阻抗值,那就更方便了,实部阻抗值标在阻抗圆上,虚部阻抗值写在电抗弧末端。我们标图的时候,不用换算,先根据实部阻抗值找到对应的阻抗圆,再沿着阻抗圆去查对应虚部阻抗值的电抗弧,然后做个记号,就OK啦~ 例如: 我要查100Ω+j50Ω的点,先找到100Ω阻抗圆,然后沿着它的轨迹上旋,到达虚部值为502的地方,然后做个标记,就是Y点。 归一化导纳值呢? 简单讲,导纳值坐标系和阻抗值的查法、标注方法完全一样。 第一步,用导纳值除以标准导纳值,得到归一化导纳值, 第二步,在等电阻线上,找到归一化实部导纳值对应的那个导纳圆。 第三步,沿着导纳圆向上或向下旋转,找与归一化虚部导纳值对应的等电纳弧。 第四步,做个记号,就算OK。 再举个栗子 0.04S+j0.02S的点在哪儿? 第一步,计算归一化实部导纳值为0.04S/0.02S=2;归一化虚部导纳值为0.02S/0.02S=1. 第二步,在等电阻线上,找到归一化导纳值为2的导纳圆。 第三步,沿着归一化导纳值为2的导纳圆上旋,转到导纳值为1的那个电纳弧上。 第四步,做个记号!即Z。 那如果图上标的是导纳值呢? 那也简单,先根据实部导纳值找到对应的导纳圆,再沿着导纳圆去查对应虚部导纳值的电纳弧,然后做个记号,就OK啦~. 例如: 我要查0.04S-j0.02S的点,先找到0.04S导纳圆,然后沿着它的轨迹下旋,到达虚部值为-0.02S的地方,然后做个标记,那就是S点。 好了~!关于怎么查史密斯圆图的事儿,我讲完了,接下来请大家做点练习,看看是否理解了。下一次我们学习如何用史密斯圆图做阻抗匹配。 读图练习 第一题,1点的阻抗值为50Ω+j50N,请问1点在图上什么位置? 第二题,2点的归一化阻抗值为0.5+j0.5,请问2点在图.上什么位置? 第三题,3点导纳值为0.04S-j0.02S,请问3点在图上什么位置? 第四题,4点归一化导纳值为0.5-j0.2,请问4点在图上什么位置? 请把上图打印出来,试着标一下,然后再看文末那个答案。 答案 图片 史密斯圆图第三节怎么用史密斯圆图软件做阻抗匹配 史密斯圆图软件做阻抗匹配有六句口诀:先设参数,后选起点,下容上感,左并右串,顺着圆走,往圆心转。 smithV3.1软件的界面,然后按着口诀顺序,一点点说。然后举个设计例子,最后讲讲注意事项。 软件界面简介 打开SmithV3.1,首先看到左侧-一个有个史密斯圆图,那是我们的操作区域。这里我要着重强调一下,电脑软件上标的都是实际阻抗值,而不是归一化阻抗值,所以我们做阻抗匹配的时候不用换算数据了,但是要在匹配之前设置好频率值和标准电阻值,否则做出来的数据会和现实完全对不上的。 图片 右侧有几个重要窗口,最顶上的是Schematic窗口,当我们在左侧史密斯圆图上用鼠标做匹配的时候,这个窗口里会显示相应的电路图,比如我图.上画的这个匹配路径对应到电路上,就是先双联13.9p电容,再并联23.4nH电感。 图片 下方这个Cursor窗口也很重要哦。它里面标注的是光标所在点的具体参数,这其中最重要的当然是VSWR驻波比、和Z阻抗值, Zo标准电阻值, Freq频率值。 这其中后两项参数是需要我们在做匹配之前要手工输入的。 先设参数,再选起点, 先设参数:就是要设置工作频率和标准阻抗这两个值。 再选起点:就是要把阻抗点的具体位置设到史密斯圆图.上。 我们点击图片 这个图标,然后在 图片 这个页面的左下角General这输入标准阻抗值50Ω,在datapoint那儿输入工作频率,默认设置是500MHz。 图片 然后我们点击 图片 按钮,软件都会弹出这个窗口。 图片 我们在这选中impedance (Ω), 并在下面两个空里填写阻抗点的实部阻抗值和虚部阻抗值,这就样就设好起点了. 然后呢?然后就该我们添加元件了。 下容上感,左并右串 我们点击工具栏的这个部分,就可以在阻抗点上连接电容或电感了。 图片 怎么接线呢?这有两句口诀:下容上感,左并右串。那我们参照下图看一看。 图片 ”下容上感”,是说史密斯圆图的电阻线上方是电感区,下方是电容区,因此呢,我们要往下移动阻抗点,就得接电容;要往上移动阻抗点,就得接电感。那具体是串电容还是并电容,又或者是串电感还是并电感呢?我们来看下一-句左并右串。 “左并右串”,是说如果我们要沿着左侧这组蓝色的导纳圆移动阻抗点,就点击“并联元件”,也就是点工具栏上这两个按钮 图片 ;如果我们要沿着右侧这组红色的阻抗圆移动阻抗点,就点击“串联元件”,就点工具栏上的这两个按钮 图片 顺着圆走,往圆心转 那我们究竟怎么走到标准阻抗点呢?那第五句和第六句说的问题。 所谓“顺着圆走”,是说顺着右侧的50阻抗圆或顺着左侧那个20mS导纳圆走,为什么呢?因为这两个圆都与匹配点相切,所以无论我们走什么路径去匹配点,都得经过其中-一个圆,而“往圆心转”是说我们无论沿着.上面哪条弧线旋转,最终都是往史密斯圆图的中心旋转。等我们把阻抗点挪到自己认为合适的位置后,右侧Schemat窗口里也就画出来了我们的阻抗匹配电路。 一个栗子 某功率放大器的要放大100M信号,其输入阻抗值为252+j352,而信号源阻抗为50Q,现在要在 两者之间接一个匹配电路,为此我要如此操作。 先设参数:我们点击 图片 这个图标,然后在 图片 这个页面的左下角General这输入标准阻抗值50Ω,在Datapoint那儿输入工作频率100MHz。然后点ok按钮。 图片 再选起点:我们点击 图片 按钮,软件都会弹出这个窗口 图片 我们在这选中impedance,然后在下面的两个空里填入实部值25欧姆、虚部值+35欧姆,点ok就行了。这时候我们发现,史密斯圆图上会出现一个方形坐标点DP1. 图片 下容上感,左并右串。顺着圆走,往圆心转那接下来,我们就要通过连接元件,改变阻抗点的阻抗了。由于DP1位于史密斯圆图的电感区,所以我们要让他往下转,那旋转是有很多路径,我们先说最简单的,从DP1直接转到20mS导纳圆上,在顺着20mS导纳圆上继续下旋到圆心。当然这是我们的设想,实际不一定能实现,我们就先以此为目标,试试看吧。 那由于我是要向下移动阻抗点,也就是向电容区移动阻抗点,所以我要连接电容。由于我是沿着右侧的25欧姆阻抗线向下移动,左并右串,所以我应该选串联电容,也就是点 图片 我们点了串联电容按钮之后,会发现光标不能自由移动了,它只能是沿着红色的25欧姆阻抗圆移动,这就是smithV3.1软件的便利之处,他能防止我们走错路。那我们按软件限定的路线,沿着25欧姆阻抗圆下旋,发现有两次机会转到20mS导纳线上,我选哪一次呢?我们设想中是选第一次机会,但是我看到右侧Schenatic窗口里,要添加的串联电容是155pf就改主意了,因为这是个非标容量,说白了我让谁买也买不来这型号的电容。于是我继续沿着25欧姆阻抗圆向下旋转,到第二个交汇点去碰碰运气。那我到达25欧姆阻抗线与20mS导纳线第二次相交的地方一看,发现右侧Schenatic窗口里显示的是串联的是26p电容,这个参数与27p极为接近,那这容量的电容有地方买,所以我就 在这儿点下鼠标左键,图片上就出现了DP2点。 那接下来,我是要由DP2沿着20mS导纳圆上旋对吧?因为下容上感,所以我们要接电感,因为左并右串,我们要沿着左侧圆走,所以要并联电感,那我们点 图片 和第一步一样,我们还是要沿着电脑规划的线路边走边看,走到圆心时,我们发现右侧Schemat窗口里,显示的电感值是80nH左右,这个电感没啥制作难度,所以我就再次点击鼠标左键,史密斯圆图中心附近就会多出来一个DP3。 也就是路径4这个图。 图片 那我们到了这个DP3又意味着什么呢?我们别挪动光标,我们把鼠标停在DP3上,把视线挪到Cursor这个小窗上,在这我们会看到Q值0.005, VSWR1.03等参数。显然,VSWR约等于1,是个 很好的结果,实际小信号的SWR匹配到1 .5就以很好了。而大功率的通讯设备,需要尽量往小了做,做到1.2以下。 两个问题 首先,我们还可以有别的匹配路径么?当然可以,条条大路通罗马。 刚才我们走的路径是先串后并,用了一一个电容一个电感,走20mS导纳圆到的中点,也就是图上的路径1。那如果我们改走右边的509阻抗圆行么?行啊,完全可以。我们可以先并联16pf电容,再串联51pf电容。这就是路径2。 那还有别的路可走么?有啊,比如我们可以选用阻抗匹配变压器,也就是先串联47pf电容,下旋到电阻线,再使用1:1 .4的阻抗变压器,沿着等Q值弧线(也就是从短路点、断路点之间的蓝色弧线,参见文末的图例)向右移动到圆心,这就是路径39。 那还有别的么?有啊,我们还可以接电阻,走等虚部弧。比如我们可以走路径4,即先串联电阻,从DP1进到右边50Q阻抗圆.上,然后再串电容沿着50Q阻抗圆继续下旋,这也是可以的。只不过要注意两个问题,第- -点, 我们接电阻走等虚部线的时候,虽然匹配上了,但是会增加电路损耗的。第二点,由于现实中可没有合用的负阻元件卖,所以我们做匹配时绝不能沿着等虚部线退着走,也就是说不能接负阻器件,这- -点smith3.1软件考虑到了,他会限制我们那么做,但是其他软件或纸本的史密斯圆图就得注意这问题。 图片 其次,如果我们做匹配时转不到史密斯圆图的正中心(不太可能),或者实际制作中对不到史密斯圆图中心(很常见),那又 意味着什么? 请看下图,图上这些棕色圆圈是等VSWR圆,也就是等驻波比圆,这个驻波比圆的特点是越靠近圆心,驻波比越小,比如圆心那- -点是1.也就是说输入端送出进去多少信号,负载端就吸收多少信号,能量一点没糟践;驻波比1.5, 意味着有4%的功率撞到输出端又撞回输入端了。驻波比29,就意味着有11%的能量撞回去了。通常来说,小信号阻抗匹配电路对驻波比要求比较低,达到1.5就算良好匹配了。如果体积有限制,还可以进一步放宽一些。 但是如果是强信号,电台天线那种场合,SWR控制的就比较严格了,大功率电台,通信基站要控制在1.2以内。 图片
嵌入式&系统
# 天线设计
# 电子线路
刘航宇
4年前
0
4,032
7
Verilog实现FIFO的设计
FIFO(First In First Out)是异步数据传输时经常使用的存储器。该存储器的特点是数据先进先出(后进后出)。其实,多位宽数据的异步传输问题,无论是从快时钟到慢时钟域,还是从慢时钟到快时钟域,都可以使用 FIFO 处理。 完整的 FIFO 设计见附件,包括输入数据位宽小于输出数据位宽时的异步设计和仿真。 源代码FIFO.zip 下载地址:https://wwu.lanzoub.com/iIdvu0am46cf 提取码: 目录 FIFO 原理工作流程 读写时刻 读空状态 写满状态 FIFO 设计设计要求 双口 RAM 设计 计数器设计 FIFO 设计FIFO 调用 testbench 仿真分析 FIFO 原理 工作流程 复位之后,在写时钟和状态信号的控制下,数据写入 FIFO 中。RAM 的写地址从 0 开始,每写一次数据写地址指针加一,指向下一个存储单元。当 FIFO 写满后,数据将不能再写入,否则数据会因覆盖而丢失。 FIFO 数据为非空、或满状态时,在读时钟和状态信号的控制下,可以将数据从 FIFO 中读出。RAM 的读地址从 0 开始,每读一次数据读地址指针加一,指向下一个存储单元。当 FIFO 读空后,就不能再读数据,否则读出的数据将是错误的。 FIFO 的存储结构为双口 RAM,所以允许读写同时进行。典型异步 FIFO 结构图如下所示。端口及内部信号将在代码编写时进行说明。 图片 读写时刻 关于写时刻,只要 FIFO 中数据为非满状态,就可以进行写操作;如果 FIFO 为满状态,则禁止再写数据。 关于读时刻,只要 FIFO 中数据为非空状态,就可以进行读操作;如果 FIFO 为空状态,则禁止再读数据。 不管怎样,一段正常读写 FIFO 的时间段,如果读写同时进行,则要求写 FIFO 速率不能大于读速率。 读空状态 开始复位时,FIFO 没有数据,空状态信号是有效的。当 FIFO 中被写入数据后,空状态信号拉低无效。当读数据地址追赶上写地址,即读写地址都相等时,FIFO 为空状态。 因为是异步 FIFO,所以读写地址进行比较时,需要同步打拍逻辑,就需要耗费一定的时间。所以空状态的指示信号不是实时的,会有一定的延时。如果在这段延迟时间内又有新的数据写入 FIFO,就会出现空状态指示信号有效,但是 FIFO 中其实存在数据的现象。 严格来讲该空状态指示是错误的。但是产生空状态的意义在于防止读操作对空状态的 FIFO 进行数据读取。产生空状态信号时,实际 FIFO 中有数据,相当于提前判断了空状态信号,此时不再进行读 FIFO 数据操作也是安全的。所以,该设计从应用上来说是没有问题的。 写满状态 开始复位时,FIFO 没有数据,满信号是无效的。当 FIFO 中被写入数据后,此时读操作不进行或读速率相对较慢,只要写数据地址超过读数据地址一个 FIFO 深度时,便会产生满状态信号。此时写地址和读地址也是相等的,但是意义是不一样的。 图片 此时经常使用多余的 1bit 分别当做读写地址的拓展位,来区分读写地址相同的时候,FIFO 的状态是空还是满状态。当读写地址与拓展位均相同的时候,表明读写数据的数量是一致的,则此时 FIFO 是空状态。如果读写地址相同,拓展位为相反数,表明写数据的数量已经超过读数据数量的一个 FIFO 深度了,此时 FIFO 是满状态。当然,此条件成立的前提是空状态禁止读操作、满状态禁止写操作。 同理,由于异步延迟逻辑的存在,满状态信号也不是实时的。但是也相当于提前判断了满状态信号,此时不再进行写 FIFO 操作也不会影响应用的正确性。 FIFO 设计 设计要求 为设计应用于各种场景的 FIFO,这里对设计提出如下要求: (1) FIFO 深度、宽度参数化,输出空、满状态信号,并输出一个可配置的满状态信号。当 FIFO 内部数据达到设置的参数数量时,拉高该信号。 (2) 输入数据和输出数据位宽可以不一致,但要保证写数据、写地址位宽与读数据、读地址位宽的一致性。例如写数据位宽 8bit,写地址位宽为 6bit(64 个数据)。如果输出数据位宽要求 32bit,则输出地址位宽应该为 4bit(16 个数据)。 (3) FIFO 是异步的,即读写控制信号来自不同的时钟域。输出空、满状态信号之前,读写地址信号要用格雷码做同步处理,通过减少多位宽信号的翻转来减少打拍法同步时数据的传输错误。 格雷码与二进制之间的转换如下图所示。 图片 双口 RAM 设计 RAM 端口参数可配置,读写位宽可以不一致。建议 memory 数组定义时,以长位宽地址、短位宽数据的参数为参考,方便数组变量进行选择访问。 Verilog 描述如下。 module ramdp #( parameter AWI = 5 , parameter AWO = 7 , parameter DWI = 64 , parameter DWO = 16 ) ( input CLK_WR , //写时钟 input WR_EN , //写使能 input [AWI-1:0] ADDR_WR ,//写地址 input [DWI-1:0] D , //写数据 input CLK_RD , //读时钟 input RD_EN , //读使能 input [AWO-1:0] ADDR_RD ,//读地址 output reg [DWO-1:0] Q //读数据 ); //输出位宽大于输入位宽,求取扩大的倍数及对应的位数 parameter EXTENT = DWO/DWI ; parameter EXTENT_BIT = AWI-AWO > 0 ? AWI-AWO : 'b1 ; //输入位宽大于输出位宽,求取缩小的倍数及对应的位数 parameter SHRINK = DWI/DWO ; parameter SHRINK_BIT = AWO-AWI > 0 ? AWO-AWI : 'b1; genvar i ; generate //数据位宽展宽(地址位宽缩小) if (DWO >= DWI) begin //写逻辑,每时钟写一次 reg [DWI-1:0] mem [(1<<AWI)-1 : 0] ; always @(posedge CLK_WR) begin if (WR_EN) begin mem[ADDR_WR] <= D ; end end //读逻辑,每时钟读 4 次 for (i=0; i<EXTENT; i=i+1) begin always @(posedge CLK_RD) begin if (RD_EN) begin Q[(i+1)*DWI-1: i*DWI] <= mem[(ADDR_RD*EXTENT) + i ] ; end end end end //================================================= //数据位宽缩小(地址位宽展宽) else begin //写逻辑,每时钟写 4 次 reg [DWO-1:0] mem [(1<<AWO)-1 : 0] ; for (i=0; i<SHRINK; i=i+1) begin always @(posedge CLK_WR) begin if (WR_EN) begin mem[(ADDR_WR*SHRINK)+i] <= D[(i+1)*DWO -1: i*DWO] ; end end end //读逻辑,每时钟读 1 次 always @(posedge CLK_RD) begin if (RD_EN) begin Q <= mem[ADDR_RD] ; end end end endgenerate endmodule计数器设计 计数器用于产生读写地址信息,位宽可配置,不需要设置结束值,让其溢出后自动重新计数即可。Verilg 描述如下。 module ccnt #(parameter W ) ( input rstn , input clk , input en , output [W-1:0] count ); reg [W-1:0] count_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin count_r <= 'b0 ; end else if (en) begin count_r <= count_r + 1'b1 ; end end assign count = count_r ; endmoduleFIFO 设计 该模块为 FIFO 的主体部分,产生读写控制逻辑,并产生空、满、可编程满状态信号。 鉴于篇幅原因,这里只给出读数据位宽大于写数据位宽的逻辑代码,写数据位宽大于读数据位宽的代码描述详见附件。 module fifo #( parameter AWI = 5 , parameter AWO = 3 , parameter DWI = 4 , parameter DWO = 16 , parameter PROG_DEPTH = 16) //可设置深度 ( input rstn, //读写使用一个复位 input wclk, //写时钟 input winc, //写使能 input [DWI-1: 0] wdata, //写数据 input rclk, //读时钟 input rinc, //读使能 output [DWO-1 : 0] rdata, //读数据 output wfull, //写满标志 output rempty, //读空标志 output prog_full //可编程满标志 ); //输出位宽大于输入位宽,求取扩大的倍数及对应的位数 parameter EXTENT = DWO/DWI ; parameter EXTENT_BIT = AWI-AWO ; //输出位宽小于输入位宽,求取缩小的倍数及对应的位数 parameter SHRINK = DWI/DWO ; parameter SHRINK_BIT = AWO-AWI ; //==================== push/wr counter =============== wire [AWI-1:0] waddr ; wire wover_flag ; //多使用一位做写地址拓展 ccnt #(.W(AWI+1)) u_push_cnt( .rstn (rstn), .clk (wclk), .en (winc && !wfull), //full 时禁止写 .count ({wover_flag, waddr}) ); //============== pop/rd counter =================== wire [AWO-1:0] raddr ; wire rover_flag ; //多使用一位做读地址拓展 ccnt #(.W(AWO+1)) u_pop_cnt( .rstn (rstn), .clk (rclk), .en (rinc & !rempty), //empyt 时禁止读 .count ({rover_flag, raddr}) ); //============================================== //窄数据进,宽数据出 generate if (DWO >= DWI) begin : EXTENT_WIDTH //格雷码转换 wire [AWI:0] wptr = ({wover_flag, waddr}>>1) ^ ({wover_flag, waddr}) ; //将写数据指针同步到读时钟域 reg [AWI:0] rq2_wptr_r0 ; reg [AWI:0] rq2_wptr_r1 ; always @(posedge rclk or negedge rstn) begin if (!rstn) begin rq2_wptr_r0 <= 'b0 ; rq2_wptr_r1 <= 'b0 ; end else begin rq2_wptr_r0 <= wptr ; rq2_wptr_r1 <= rq2_wptr_r0 ; end end //格雷码转换 wire [AWI-1:0] raddr_ex = raddr << EXTENT_BIT ; wire [AWI:0] rptr = ({rover_flag, raddr_ex}>>1) ^ ({rover_flag, raddr_ex}) ; //将读数据指针同步到写时钟域 reg [AWI:0] wq2_rptr_r0 ; reg [AWI:0] wq2_rptr_r1 ; always @(posedge wclk or negedge rstn) begin if (!rstn) begin wq2_rptr_r0 <= 'b0 ; wq2_rptr_r1 <= 'b0 ; end else begin wq2_rptr_r0 <= rptr ; wq2_rptr_r1 <= wq2_rptr_r0 ; end end //格雷码反解码 //如果只需要空、满状态信号,则不需要反解码 //因为可编程满状态信号的存在,地址反解码后便于比较 reg [AWI:0] wq2_rptr_decode ; reg [AWI:0] rq2_wptr_decode ; integer i ; always @(*) begin wq2_rptr_decode[AWI] = wq2_rptr_r1[AWI]; for (i=AWI-1; i>=0; i=i-1) begin wq2_rptr_decode[i] = wq2_rptr_decode[i+1] ^ wq2_rptr_r1[i] ; end end always @(*) begin rq2_wptr_decode[AWI] = rq2_wptr_r1[AWI]; for (i=AWI-1; i>=0; i=i-1) begin rq2_wptr_decode[i] = rq2_wptr_decode[i+1] ^ rq2_wptr_r1[i] ; end end //读写地址、拓展位完全相同是,为空状态 assign rempty = (rover_flag == rq2_wptr_decode[AWI]) && (raddr_ex >= rq2_wptr_decode[AWI-1:0]); //读写地址相同、拓展位不同,为满状态 assign wfull = (wover_flag != wq2_rptr_decode[AWI]) && (waddr >= wq2_rptr_decode[AWI-1:0]) ; //拓展位一样时,写地址必然不小于读地址 //拓展位不同时,写地址部分比如小于读地址,实际写地址要增加一个FIFO深度 assign prog_full = (wover_flag == wq2_rptr_decode[AWI]) ? waddr - wq2_rptr_decode[AWI-1:0] >= PROG_DEPTH-1 : waddr + (1<<AWI) - wq2_rptr_decode[AWI-1:0] >= PROG_DEPTH-1; //双口 ram 例化 ramdp #( .AWI (AWI), .AWO (AWO), .DWI (DWI), .DWO (DWO)) u_ramdp ( .CLK_WR (wclk), .WR_EN (winc & !wfull), //写满时禁止写 .ADDR_WR (waddr), .D (wdata[DWI-1:0]), .CLK_RD (rclk), .RD_EN (rinc & !rempty), //读空时禁止读 .ADDR_RD (raddr), .Q (rdata[DWO-1:0]) ); end //============================================== //big in and small out /* else begin: SHRINK_WIDTH …… end */ endgenerate endmoduleFIFO 调用 下面可以调用设计的 FIFO,完成多位宽数据传输的异步处理。 写数据位宽为 4bit,写深度为 32。 读数据位宽为 16bit,读深度为 8,可配置 full 深度为 16。 module fifo_s2b( input rstn, input [4-1: 0] din, //异步写数据 input din_clk, //异步写时钟 input din_en, //异步写使能 output [16-1 : 0] dout, //同步后数据 input dout_clk, //同步使用时钟 input dout_en ); //同步数据使能 wire fifo_empty, fifo_full, prog_full ; wire rd_en_wir ; wire [15:0] dout_wir ; //读空状态时禁止读,否则一直读 assign rd_en_wir = fifo_empty ? 1'b0 : 1'b1 ; fifo #(.AWI(5), .AWO(3), .DWI(4), .DWO(16), .PROG_DEPTH(16)) u_buf_s2b( .rstn (rstn), .wclk (din_clk), .winc (din_en), .wdata (din), .rclk (dout_clk), .rinc (rd_en_wir), .rdata (dout_wir), .wfull (fifo_full), .rempty (fifo_empty), .prog_full (prog_full)); //缓存同步后的数据和使能 reg dout_en_r ; always @(posedge dout_clk or negedge rstn) begin if (!rstn) begin dout_en_r <= 1'b0 ; end else begin dout_en_r <= rd_en_wir ; end end assign dout = dout_wir ; assign dout_en = dout_en_r ; endmoduletestbench `timescale 1ns/1ns `define SMALL2BIG module test ; `ifdef SMALL2BIG reg rstn ; reg clk_slow, clk_fast ; reg [3:0] din ; reg din_en ; wire [15:0] dout ; wire dout_en ; //reset initial begin clk_slow = 0 ; clk_fast = 0 ; rstn = 0 ; #50 rstn = 1 ; end //读时钟 clock_slow 较快于写时钟 clk_fast 的 1/4 //保证读数据稍快于写数据 parameter CYCLE_WR = 40 ; always #(CYCLE_WR/2/4) clk_fast = ~clk_fast ; always #(CYCLE_WR/2-1) clk_slow = ~clk_slow ; //data generate initial begin din = 16'h4321 ; din_en = 0 ; wait (rstn) ; //(1) 测试 full、prog_full、empyt 信号 force test.u_data_buf2.u_buf_s2b.rinc = 1'b0 ; repeat(32) begin @(negedge clk_fast) ; din_en = 1'b1 ; din = {$random()} % 16; end @(negedge clk_fast) din_en = 1'b0 ; //(2) 测试数据读写 #500 ; rstn = 0 ; #10 rstn = 1 ; release test.u_data_buf2.u_buf_s2b.rinc; repeat(100) begin @(negedge clk_fast) ; din_en = 1'b1 ; din = {$random()} % 16; end //(3) 停止读取再一次测试 empyt、full、prog_full 信号 force test.u_data_buf2.u_buf_s2b.rinc = 1'b0 ; repeat(18) begin @(negedge clk_fast) ; din_en = 1'b1 ; din = {$random()} % 16; end end fifo_s2b u_data_buf2( .rstn (rstn), .din (din), .din_clk (clk_fast), .din_en (din_en), .dout (dout), .dout_clk (clk_slow), .dout_en (dout_en)); `else `endif //stop sim initial begin forever begin #100; if ($time >= 5000) $finish ; end end endmodule仿真分析 根据 testbench 中的 3 步测试激励,分析如下: 测试 (1) : FIFO 端口及一些内部信号时序结果如下。 由图可知,FIFO 内部开始写数据,空状态信号拉低之前有一段时间延迟,这是同步读写地址信息导致的。 由于此时没有进行读 FIFO 操作,相对于写数据操作,full 和 prog_full 拉高几乎没有延迟。 图片 测试 (2) : FIFO 同时进行读写时,数字顶层异步处理模块的端口信号如下所示,两图分别显示了数据开始传输、结束传输时的读取过程。 由图可知,数据在开始、末尾均能正确传输,完成了不同时钟域之间多位宽数据的异步处理。 图片 图片 测试 (3) :整个 FIFO 读写行为及读停止的时序仿真图如下所示。 由图可知,读写同时进行时,读空状态信号 rempty 会拉低,表明 FIFO 中有数据写入。一方面读数据速率稍高于写速率,且数据之间传输会有延迟,所以中间过程中 rempty 会有拉高的行为。 读写过程中,full 与 prog_full 信号一直为低,说明 FIFO 中数据并没有到达一定的数量。当停止读操作后,两个 full 信号不久便拉高,表明 FIFO 已满。仔细对比读写地址信息,FIFO 行为没有问题。 图片
FPGA&ASIC
# ASIC/FPGA
刘航宇
4年前
0
867
2
Verilog教程-CIC 滤波器设计
积分梳状滤波器(CIC,Cascaded Integrator Comb),一般用于数字下变频(DDC)和数字上变频(DUC)系统。CIC 滤波器结构简单,没有乘法器,只有加法器、积分器和寄存器,资源消耗少,运算速率高,可实现高速滤波,常用在输入采样率最高的第一级,在多速率信号处理系统中具有着广泛应用。 目录 DDC 原理 带通采样定理 DDC 频谱搬移 单级 CIC 滤波器 积分器 梳状器 抽取器 参数说明 多级 CIC 滤波器 CIC 滤波器设计 积分器设计 抽取器设计 梳状器设计 顶层例化 仿真结果 DDC 原理 DDC 工作原理 DDC 主要由本地振荡器(NCO) 、混频器、滤波器等组成,如下图所示。 图片 DDC 将中频信号与振荡器产生的载波信号进行混频,信号中心频率被搬移,再经过抽取滤波,恢复原始信号,实现了下变频功能。 中频数据采样时,需要很高的采样频率来确保 ADC(模数转换器)采集到信号的信噪比。经过数字下变频后,得到的基带信号采样频率仍然是 ADC 采样频率,所以数据率很高。此时基带信号的有效带宽往往已经远小于采样频率,所以利用抽取、滤波进行数据速率的转换,使采样率降低,避免资源的浪费和设计的困难,就成为 DDC 不可缺少的一部分。 而采用 CIC 滤波器进行数据处理,是 DDC 抽取滤波部分最常用的方法。 带通采样定理 在 DDC 系统中,输入的中频载波信号会根据载波频率进行频移,得到一个带通信号。如果此时仍然采用奈奎斯特采样定理,即采样频率为带通信号最高频率的两倍,那么此时所需的采样频率将会很高,设计会变的复杂。此时可按照带通采样定理来确定抽样频率。 带通采样定理:一个频带限制在$\left(f_{L}, f_{H}\right)$的连续带通信号,带宽为$\mathrm{B}=f_{H}-f_{L}$。令$0 \leq m \leq N-1$,其中 N 为不大于$f_{H} / B$的最大正整数,如果采样频率满足条件:$\frac{2 f_{H}}{m+1} \leq F_{S} \leq \frac{2 f_{L}}{m}$,$0 \leq m \leq N-1$ 则该信号完全可以由其采样值无失真的重建。 当 m=1 时,带通采样定理便是奈奎斯特采样定理。 带通采样定理的另一种描述方式为:若信号最高频率为信号带宽的整数倍,采样频率只需大于信号带宽的两倍即可,此时不会发生频谱混叠。 所以,可以认为采样频率的一半是 CIC 滤波器的截止频率。 DDC 频谱搬移 例如一个带宽信号中心频率为 60MHz,带宽为 8MHz, 则频率范围为 56MHz ~ 64MHz,m 的可取值范围为 0 ~ 7。取 m=1, 则采样频率范围为 64MHz ~ 112MHz。 取采样频率为 80MHz,设 NCO 中心频率为 20 MHz,下面讨论复信号频谱搬移示意图。 (1)考虑频谱的对称性,输入复信号的频谱示意图如下: 图片 (2)80MHz 采样频率采样后,56~64MHz 的频带被搬移到了 -24~ -16MHz 与 136 ~ 144MHz(高于采样频率被滤除)的频带处,-64~ -56MHz 的频带被搬移到 -144~ -136MHz(高于采样频率被滤除)与 16~24MHz 的频带处。 采样后频带分布如下: 图片 (3)信号经过 20MHz NCO 的正交电路后, -24~ -16MHz 的频带被搬移到 -4~4MHz 与 -44~ -36MHz 的频带处,16~24MHz 的频带被搬移到 -4~4MHz 与 36~44MHz 的频带处,如下所示。 图片 (4)此时中频输入的信号已经被搬移到零中频基带处。 -44~ -36MHz 和 36~44MHz 的带宽信号是不需要的,可以滤除;-4~4MHz 的零中频信号数据速率仍然是 80MHz,可以进行抽取降低数据速率。而 CIC 滤波,就是要完成这个过程。 上述复习了很多数字信号处理的内容,权当抛 DDC 的砖,引 CIC 的玉。 CIC 滤波器原理 单级 CIC 滤波器 设滤波器抽取倍数为 D,则单级滤波器的冲激响应为: 图片 对其进行 z 变换,可得单级 CIC 滤波器的系统函数为: 图片 令 图片 可以看出,单级 CIC 滤波器包括两个基本组成部分:积分部分和梳状部分,结构图如下: 图片 积分器 积分器是一个单级点的 IIR(Infinite Impulse Response,无限长脉冲冲激响应)滤波器,且反馈系数为 1,其状态方程和系统函数分别为: 图片 图片 梳状器 梳状器是一个 FIR 滤波器,其状态方程和系统函数分别为: 图片 图片 抽取器 在积分器之后,还有一个抽取器,抽取倍数与梳状器的延时参数是一致的。利用 z 变换的性质进行恒等变换,将抽取器移动到积分器与梳状器之间,可得到单级 CIC 滤波器结构,如下所示。 图片 参数说明 CIC 滤波器结构变换之前的参数 D 可以理解为梳状滤波器的延时或阶数;变换之后,D 的含义 变为抽取倍数,而此时梳状滤波器的延时为 1,即阶数为 1。 很多学者会引入一个变量 M,表示梳状器每一级的延时,此时梳妆部分的延时就不为 1 了。那么梳状器的系统函数就变为: 图片 其实把 DM 整体理解为单级滤波器延时,或者抽取倍数,也都是可以的。可能实现的方式或结构不同,但是最后的结果都是一样的。本次设计中,单级滤波器延时都为 M=1,即抽取倍数与滤波延时相同。 多级 CIC 滤波器 单级 CIC 滤波器的阻带衰减较差,为了提高滤波效果,抽取滤波时往往会采用多级 CIC 滤波器级联的结构。 实现多级直接级联的 CIC 滤波器在设计和资源上并不是最优的方式,需要对其结构进行调整。如下所示,将积分器和梳状滤波器分别移至一组,并将抽取器移到梳状滤波器之前。先抽取再进行滤波,可以减少数据处理的长度,节约硬件资源。 图片 当然,级联数越大,旁瓣抑制越好,但是通带内的平坦度也会变差。所以级联数不宜过多,一般最多 5 级。 CIC 滤波器设计 设计说明 CIC 滤波器本质上就是一个简单的低通滤波器,截止频率为采样频率除以抽取倍数后的一半。输入数据信号仍然是 7.5MHz 和 250KHz,采样频率 50MHz。抽取倍数设置为 5,则截止频率为 5MHz,小于 7.5MHz,可以滤除 7.5MHz 的频率成分。设计参数如下: 输入频率: 7.5MHz 和 250KHz 采样频率: 50MHz 阻带: 5MHz 阶数: 1(M=1) 级数: 3(N=3) 关于积分时中间数据信号的位宽,很多地方给出了不同的计算方式,计算结果也大相径庭。这里总结一下使用最多的计算方式: 图片 其中,D 为抽取倍数,M 为滤波器阶数,N 为滤波器级数。抽取倍数为 5,滤波器阶数为 1,滤波器级联数为 3,取输入信号数据位宽为 12bit,对数部分向上取整,则积分后数据不溢出的中间信号位宽为 21bit。 为了更加宽裕的设计,滤波器阶数如果理解为未变换结构前的多级 CIC 滤波器直接型结构,则滤波器阶数可以认为是 5,此时中间信号最大位宽为 27bit。 积分器设计 根据输入数据的有效信号的控制,积分器做一个简单的累加即可,注意数据位宽。 //3 stages integrator module integrator #(parameter NIN = 12, parameter NOUT = 21) ( input clk , input rstn , input en , input [NIN-1:0] din , output valid , output [NOUT-1:0] dout) ; reg [NOUT-1:0] int_d0 ; reg [NOUT-1:0] int_d1 ; reg [NOUT-1:0] int_d2 ; wire [NOUT-1:0] sxtx = {{(NOUT-NIN){1'b0}}, din} ; //data input enable delay reg [2:0] en_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin en_r <= 'b0 ; end else begin en_r <= {en_r[1:0], en}; end end //integrator //stage1 always @(posedge clk or negedge rstn) begin if (!rstn) begin int_d0 <= 'b0 ; end else if (en) begin int_d0 <= int_d0 + sxtx ; end end //stage2 always @(posedge clk or negedge rstn) begin if (!rstn) begin int_d1 <= 'b0 ; end else if (en_r[0]) begin int_d1 <= int_d1 + int_d0 ; end end //stage3 always @(posedge clk or negedge rstn) begin if (!rstn) begin int_d2 <= 'b0 ; end else if (en_r[1]) begin int_d2 <= int_d2 + int_d1 ; end end assign dout = int_d2 ; assign valid = en_r[2]; endmodule抽取器设计 抽取器设计时,对积分器输出的数据进行计数,然后间隔 5 个数据进行抽取即可。 module decimation #(parameter NDEC = 21) ( input clk, input rstn, input en, input [NDEC-1:0] din, output valid, output [NDEC-1:0] dout); reg valid_r ; reg [2:0] cnt ; reg [NDEC-1:0] dout_r ; //counter always @(posedge clk or negedge rstn) begin if (!rstn) begin cnt <= 3'b0; end else if (en) begin if (cnt==4) begin cnt <= 'b0 ; end else begin cnt <= cnt + 1'b1 ; end end end //data, valid always @(posedge clk or negedge rstn) begin if (!rstn) begin valid_r <= 1'b0 ; dout_r <= 'b0 ; end else if (en) begin if (cnt==4) begin valid_r <= 1'b1 ; dout_r <= din; end else begin valid_r <= 1'b0 ; end end end assign dout = dout_r ; assign valid = valid_r ; endmodule梳状器设计 梳状滤波器就是简单的一阶 FIR 滤波器,每一级的 FIR 滤波器对数据进行一个时钟延时,然后做相减即可。因为系数为 ±1,所以不需要乘法器。 module comb #(parameter NIN = 21, parameter NOUT = 17) ( input clk, input rstn, input en, input [NIN-1:0] din, input valid, output [NOUT-1:0] dout); //en delay reg [5:0] en_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin en_r <= 'b0 ; end else if (en) begin en_r <= {en_r[5:0], en} ; end end reg [NOUT-1:0] d1, d1_d, d2, d2_d, d3, d3_d ; //stage 1, as fir filter, shift and add(sub), //no need for multiplier always @(posedge clk or negedge rstn) begin if (!rstn) d1 <= 'b0 ; else if (en) d1 <= din ; end always @(posedge clk or negedge rstn) begin if (!rstn) d1_d <= 'b0 ; else if (en) d1_d <= d1 ; end wire [NOUT-1:0] s1_out = d1 - d1_d ; //stage 2 always @(posedge clk or negedge rstn) begin if (!rstn) d2 <= 'b0 ; else if (en) d2 <= s1_out ; end always @(posedge clk or negedge rstn) begin if (!rstn) d2_d <= 'b0 ; else if (en) d2_d <= d2 ; end wire [NOUT-1:0] s2_out = d2 - d2_d ; //stage 3 always @(posedge clk or negedge rstn) begin if (!rstn) d3 <= 'b0 ; else if (en) d3 <= s2_out ; end always @(posedge clk or negedge rstn) begin if (!rstn) d3_d <= 'b0 ; else if (en) d3_d <= d3 ; end wire [NOUT-1:0] s3_out = d3 - d3_d ; //tap the output data for better display reg [NOUT-1:0] dout_r ; reg valid_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin dout_r <= 'b0 ; valid_r <= 'b0 ; end else if (en) begin dout_r <= s3_out ; valid_r <= 1'b1 ; end else begin valid_r <= 1'b0 ; end end assign dout = dout_r ; assign valid = valid_r ; endmodule顶层例化 按信号的流向将积分器、抽取器、梳状器分别例化,即可组成最后的 CIC 滤波器模块。 梳状滤波器的最终输出位宽一般会比输入信号小一些,这里取 17bit。当然输出位宽完全可以与输入数据的位宽一致。 module cic #(parameter NIN = 12, parameter NMAX = 21, parameter NOUT = 17) ( input clk, input rstn, input en, input [NIN-1:0] din, input valid, output [NOUT-1:0] dout); wire [NMAX-1:0] itg_out ; wire [NMAX-1:0] dec_out ; wire [1:0] en_r ; integrator #(.NIN(NIN), .NOUT(NMAX)) u_integrator ( .clk (clk), .rstn (rstn), .en (en), .din (din), .valid (en_r[0]), .dout (itg_out)); decimation #(.NDEC(NMAX)) u_decimator ( .clk (clk), .rstn (rstn), .en (en_r[0]), .din (itg_out), .dout (dec_out), .valid (en_r[1])); comb #(.NIN(NMAX), .NOUT(NOUT)) u_comb ( .clk (clk), .rstn (rstn), .en (en_r[1]), .din (dec_out), .valid (valid), .dout (dout)); endmoduletestbench testbench 编写如下,主要功能就是不间断连续的输入 250KHz 与 7.5MHz 的正弦波混合信号数据。输入的混合信号数据也可由 matlab 生成,具体过程参考《并行 FIR 滤波器设计》一节。 module test ; parameter NIN = 12 ; parameter NMAX = 21 ; parameter NOUT = NMAX ; reg clk ; reg rstn ; reg en ; reg [NIN-1:0] din ; wire valid ; wire [NOUT-1:0] dout ; //===================================== // 50MHz clk generating localparam T50M_HALF = 10000; initial begin clk = 1'b0 ; forever begin # T50M_HALF clk = ~clk ; end end //============================ // reset and finish initial begin rstn = 1'b0 ; # 30 ; rstn = 1'b1 ; # (T50M_HALF * 2 * 2000) ; $finish ; end //======================================= // read cos data into register parameter SIN_DATA_NUM = 200 ; reg [NIN-1:0] stimulus [0: SIN_DATA_NUM-1] ; integer i ; initial begin $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ; i = 0 ; en = 0 ; din = 0 ; # 200 ; forever begin @(negedge clk) begin en = 1 ; din = stimulus[i] ; if (i == SIN_DATA_NUM-1) begin i = 0 ; end else begin i = i + 1 ; end end end end cic #(.NIN(NIN), .NMAX(NMAX), .NOUT(NOUT)) u_cic ( .clk (clk), .rstn (rstn), .en (en), .din (din), .valid (valid), .dout (dout)); endmodule // test仿真结果 由下图仿真结果可知,经过 CIC 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。 但是波形不是非常完美,这与设计的截止频率、数据不是持续输出等有一定关系。 此时发现,积分器输出的数据信号也非常的不规则,这与其位宽有关系。 图片 为了更好的观察积分器输出的数据,将其位宽由 21bit 改为 34bit,仿真结果如下。 此时发现,CIC 滤波器的数据输出并没有实质性的变化,但是积分器输出的数据信号呈现锯齿状,也称之为梳状。这也是梳状滤波器名字的由来。 图片
FPGA&ASIC
刘航宇
4年前
0
1,444
2
2022-08-13
Verilog 实现并行 FIR 滤波器设计
FIR(Finite Impulse Response)滤波器是一种有限长单位冲激响应滤波器,又称为非递归型滤波器。 FIR 滤波器具有严格的线性相频特性,同时其单位响应是有限长的,因而是稳定的系统,在数字通信、图像处理等领域都有着广泛的应用。 目录 FIR 滤波器原理 并行 FIR 滤波器设计 并行设计 仿真结果 附录:matlab 使用 生成输入的混合信号 FIR 滤波器原理 FIR 滤波器是有限长单位冲击响应滤波器。直接型结构如下: 图片 FIR 滤波器本质上就是输入信号与单位冲击响应函数的卷积,表达式如下: 图片 FIR 滤波器有如下几个特性: (1) 响应是有限长序列。 (2) 系统函数在 |z| > 0 处收敛,极点全部在 z=0 处,属于因果系统。 (3) 结构上是非递归的,没有输出到输入的反馈。 (4) 输入信号相位响应是线性的,因为响应函数 h(n) 系数是对称的。 (5) 输入信号的各频率之间,相对相位差也是固定不变的。 (6) 时域卷积等于频域相乘,因此该卷积相当于筛选频谱中各频率分量的增益倍数。某些频率分量保留,某些频率分量衰减,从而实现滤波的效果。 并行 FIR 滤波器设计 设计说明 输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号,经过 FIR 滤波器后,高频信号 7.5MHz 被滤除,只保留 250KHz 的信号。设计参数如下: 输入频率: 7.5MHz 和 250KHz 采样频率: 50MHz 阻带: 1MHz ~ 6MHz 阶数: 15(N-1=15) 由 FIR 滤波器结构可知,阶数为 15 时,FIR 的实现需要 16 个乘法器,15 个加法器和 15 组延时寄存器。为了稳定第一拍的数据,可以再多用一组延时寄存器,即共用 16 组延时寄存器。由于 FIR 滤波器系数的对称性,乘法器可以少用一半,即共使用 8 个乘法器。 并行设计,就是在一个时钟周期内对 16 个延时数据同时进行乘法、加法运算,然后在时钟驱动下输出滤波值。这种方法的优点是滤波延时短,但是对时序要求比较高。 并行设计 设计中使用到的乘法器模块代码,可参考流水线式设计的乘法器。 为方便快速仿真,也可以直接使用乘号 * 完成乘法运算,设计中加入宏定义 SAFE_DESIGN 来选择使用哪种乘法器。 FIR 滤波器系数可由 matlab 生成,具体见附录。 /*********************************************************** >> V201001 : Fs:50Mhz, fstop:1Mhz-6Mhz, order: 15 ************************************************************/ `define SAFE_DESIGN module fir_guide ( input rstn, //复位,低有效 input clk, //工作频率,即采样频率 input en, //输入数据有效信号 input [11:0] xin, //输入混合频率的信号数据 output valid, //输出数据有效信号 output [28:0] yout //输出数据,低频信号,即250KHz ); //data en delay reg [3:0] en_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin en_r[3:0] <= 'b0 ; end else begin en_r[3:0] <= {en_r[2:0], en} ; end end //(1) 16 组移位寄存器 reg [11:0] xin_reg[15:0]; reg [3:0] i, j ; always @(posedge clk or negedge rstn) begin if (!rstn) begin for (i=0; i<15; i=i+1) begin xin_reg[i] <= 12'b0; end end else if (en) begin xin_reg[0] <= xin ; for (j=0; j<15; j=j+1) begin xin_reg[j+1] <= xin_reg[j] ; //周期性移位操作 end end end //Only 8 multipliers needed because of the symmetry of FIR filter coefficient //(2) 系数对称,16个移位寄存器数据进行首位相加 reg [12:0] add_reg[7:0]; always @(posedge clk or negedge rstn) begin if (!rstn) begin for (i=0; i<8; i=i+1) begin add_reg[i] <= 13'd0 ; end end else if (en_r[0]) begin for (i=0; i<8; i=i+1) begin add_reg[i] <= xin_reg[i] + xin_reg[15-i] ; end end end //(3) 8个乘法器 // 滤波器系数,已经过一定倍数的放大 wire [11:0] coe[7:0] ; assign coe[0] = 12'd11 ; assign coe[1] = 12'd31 ; assign coe[2] = 12'd63 ; assign coe[3] = 12'd104 ; assign coe[4] = 12'd152 ; assign coe[5] = 12'd198 ; assign coe[6] = 12'd235 ; assign coe[7] = 12'd255 ; reg [24:0] mout[7:0]; `ifdef SAFE_DESIGN //流水线式乘法器 wire [7:0] valid_mult ; genvar k ; generate for (k=0; k<8; k=k+1) begin mult_man #(13, 12) u_mult_paral ( .clk (clk), .rstn (rstn), .data_rdy (en_r[1]), .mult1 (add_reg[k]), .mult2 (coe[k]), .res_rdy (valid_mult[k]), //所有输出使能完全一致 .res (mout[k]) ); end endgenerate wire valid_mult7 = valid_mult[7] ; `else //如果对时序要求不高,可以直接用乘号 always @(posedge clk or negedge rstn) begin if (!rstn) begin for (i=0 ; i<8; i=i+1) begin mout[i] <= 25'b0 ; end end else if (en_r[1]) begin for (i=0 ; i<8; i=i+1) begin mout[i] <= coe[i] * add_reg[i] ; end end end wire valid_mult7 = en_r[2]; `endif //(4) 积分累加,8组25bit数据 -> 1组 29bit 数据 //数据有效延时 reg [3:0] valid_mult_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin valid_mult_r[3:0] <= 'b0 ; end else begin valid_mult_r[3:0] <= {valid_mult_r[2:0], valid_mult7} ; end end `ifdef SAFE_DESIGN //加法运算时,分多个周期进行流水,优化时序 reg [28:0] sum1 ; reg [28:0] sum2 ; reg [28:0] yout_t ; always @(posedge clk or negedge rstn) begin if (!rstn) begin sum1 <= 29'd0 ; sum2 <= 29'd0 ; yout_t <= 29'd0 ; end else if(valid_mult7) begin sum1 <= mout[0] + mout[1] + mout[2] + mout[3] ; sum2 <= mout[4] + mout[5] + mout[6] + mout[7] ; yout_t <= sum1 + sum2 ; end end `else //一步计算累加结果,但是实际中时序非常危险 reg signed [28:0] sum ; reg signed [28:0] yout_t ; always @(posedge clk or negedge rstn) begin if (!rstn) begin sum <= 29'd0 ; yout_t <= 29'd0 ; end else if (valid_mult7) begin sum <= mout[0] + mout[1] + mout[2] + mout[3] + mout[4] + mout[5] + mout[6] + mout[7]; yout_t <= sum ; end end `endif assign yout = yout_t ; assign valid = valid_mult_r[0]; endmoduletestbench testbench 编写如下,主要功能就是不间断连续的输入 250KHz 与 7.5MHz 的正弦波混合信号数据。输入的混合信号数据也可由 matlab 生成,具体见附录。 `timescale 1ps/1ps module test ; //input reg clk ; reg rst_n ; reg en ; reg [11:0] xin ; //output wire valid ; wire [28:0] yout ; parameter SIMU_CYCLE = 64'd2000 ; //50MHz 采样频率 parameter SIN_DATA_NUM = 200 ; //仿真周期 //===================================== // 50MHz clk generating localparam TCLK_HALF = 10_000; initial begin clk = 1'b0 ; forever begin # TCLK_HALF ; clk = ~clk ; end end //============================ // reset and finish initial begin rst_n = 1'b0 ; # 30 rst_n = 1'b1 ; # (TCLK_HALF * 2 * SIMU_CYCLE) ; $finish ; end //======================================= // read signal data into register reg [11:0] stimulus [0: SIN_DATA_NUM-1] ; integer i ; initial begin $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ; i = 0 ; en = 0 ; xin = 0 ; # 200 ; forever begin @(negedge clk) begin en = 1'b1 ; xin = stimulus[i] ; if (i == SIN_DATA_NUM-1) begin //周期送入数据控制 i = 0 ; end else begin i = i + 1 ; end end end end fir_guide u_fir_paral ( .xin (xin), .clk (clk), .en (en), .rstn (rst_n), .valid (valid), .yout (yout)); endmodule仿真结果 由下图仿真结果可知,经过 FIR 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。而且输出波形是连续的,能够持续输出。 但是,如红圈所示,波形起始部分呈不规则状态,对此进行放大。 图片 波形起始端放大后如下图所示,可见不规则波形的时间段,即两根竖线之间的时间间隔是 16 个时钟周期。 因为数据是串行输入,设计中使用了 16 组延时寄存器,所以滤波后的第一个正常点应该较第一个滤波数据输出时刻延迟 16 个时钟周期。即数据输出有效信号 valid 应该再延迟 16 个时钟周期,则会使输出波形更加完美。 图片 附录:matlab 使用 生成 FIR 滤波器系数 打开 matlab,在命令窗口输入命令: fdatool。 然后会打开如下窗口,按照 FIR 滤波器参数进行设置。 这里选择的 FIR 实现方法是最小二乘法(Least-squares),不同的实现方式滤波效果也不同。 图片 点击 File -> Export 将滤波器参数输出,存到变量 coef 中,如下图所示。 图片 此时 coef 变量应该是浮点型数据。对其进行一定倍数的相乘扩大,然后取其近似的定点型数据作为设计中的 FIR 滤波器参数。这里取扩大倍数为 2048,结果如下所示。 图片 生成输入的混合信号 利用 matlab 生成混合的输入信号参考代码如下。 信号为无符号定点型数据,位宽宽度为 12bit,存于文件 cosx0p25m7p5m12bit.txt。 clear all;close all;clc; %======================================================= % generating a cos wave data with txt hex format %======================================================= fc = 0.25e6 ; % 中心频率 fn = 7.5e6 ; % 杂波频率 Fs = 50e6 ; % 采样频率 T = 1/fc ; % 信号周期 Num = Fs * T ; % 周期内信号采样点数 t = (0:Num-1)/Fs ; % 离散时间 cosx = cos(2*pi*fc*t) ; % 中心频率正弦信号 cosn = cos(2*pi*fn*t) ; % 杂波信号 cosy = mapminmax(cosx + cosn) ; %幅值扩展到(-1,1) 之间 cosy_dig = floor((2^11-1) * cosy + 2^11) ; %幅值扩展到 0~4095 fid = fopen('cosx0p25m7p5m12bit.txt', 'wt') ; %写数据文件 fprintf(fid, '%x\n', cosy_dig) ; fclose(fid) ; %时域波形 figure(1); subplot(121);plot(t,cosx);hold on ; plot(t,cosn) ; subplot(122);plot(t,cosy_dig) ; %频域波形 fft_cosy = fftshift(fft(cosy, Num)) ; f_axis = (-Num/2 : Num/2 - 1) * (Fs/Num) ; figure(5) ; plot(f_axis, abs(fft_cosy)) ;
FPGA&ASIC
刘航宇
4年前
0
597
2
2022-07-14
[Verilog]ASIC/FPGA实现边沿检测电路(上升/下降沿)
目录 1、什么是边沿检测 2、采用1级触发器的边沿检测电路设计(以下降沿为例)2.1、设计方法 2.2、Verilog实现 2.3、RTL电路 2.4、Testbench 2.5、仿真结果 3、采用2级触发器的边沿检测电路(以下降沿为例)3.1、设计方法 3.2、Verilog实现 3.3、RTL电路 3.4、Testbench 3.5、仿真结果 4、参考文献 1、什么是边沿检测 边沿检测用于检测信号的上升沿或下降沿,通常用于使能信号的捕捉等场景。 2、采用1级触发器的边沿检测电路设计(以下降沿为例) 2.1、设计方法 设计波形图如下所示: 图片 各信号说明如下: sys_clk:基准时钟信号(这里设定为50MHz,周期20ns) sys_rst_n:低电平有效的复位信号 in:输入信号,需要对其进行下降沿检测 ~in:输入信号的反相信号 in_d1:对输入信号寄存一拍 in_neg:得到的下降沿指示信号,该信号为 ind1 && ~in 对上图进行分析: 信号in是我们需要对其进行下降沿检测的信号 信号~in是将信号in反向 信号in_d1是使用寄存器寄存in信号,即对其进行打拍,或者说是同步到系统时钟域下 输入信号开始为高电平,在L2处变为低电平,产生第1个下降沿,在L5出产生第2个下降沿 A处为产生的第1个下降沿指示信号,B处为产生的第2个下降沿指示信号 由此我们可以推导出边沿检测信号产生的一般方法: 将需要检测的信号寄存一拍,同步到系统时钟域下,得到信号 in_d1 将需要检测的信号反向,得到信号 ~in 将信号 in_d1 反向,得到信号 ~in_d1 通过组合逻辑电路可以得到下降沿信号 in_neg:assign in_neg = ~in && in_d1 同样通过组合逻辑电路可以得到上升沿信号 in_pos:assign in_pos = in && ~in_d1 双边沿检测就是将上两条加(或运算)起来就可以了,化简后有:双边沿信号 in_both = in ^ ind1 2.2、Verilog实现 根据上文分析不难编写Verilog代码如下: //使用1级寄存器的下降沿检测电路 module detect_1 ( input sys_clk, //时钟(设定为 50MHz) input sys_rst_n, //复位信号(n 表示低电平有效) input in, //需要进行下降沿检测的输入信号 output in_neg //输出的下降沿指示信号 ); //reg 定义 reg in_d1; //寄存一拍的信号 assign in_neg = ~in && in_d1; //组合逻辑得到下降沿 //上升沿: assign in_pos = in && ~in_d1; //双边沿: assign in_pos = in ^ in_d1; //寄存模块,将输入信号打一拍 always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) in_d1 <= 1'b0; //复位清零 else in_d1 <= in; //寄存一拍 end endmodule2.3、RTL电路 图片 上图为生成的RTL电路:该电路由一级D触发器+与逻辑门构成。 2.4、Testbench Testbench文件需要例化刚刚设计好的模块,并设置好激励。 `timescale 1ns/1ns //时间刻度:单位1ns,精度1ns module tb_detect_1(); //仿真模块 //输入reg 定义 reg sys_clk; reg sys_rst_n; reg in; //输出wire定义 wire in_neg; //设置初始化条件和输入激励 initial begin sys_clk = 1'b0; //初始时钟为0 sys_rst_n <= 1'b0; //初始复位 in <= 1'b0; //初始化输入信号 /*****以下部分为设置的激励,以产生2个下降沿*******/ #10 //10个时间单位后 sys_rst_n <= 1'b1; //拉高复位(此时复位无效) in <= 1'b1; //拉高输入 #20 //20个时间单位后 in <= 1'b0; //拉低输入,制造第1个下降沿 #80 //80个时间单位后 in <= 1'b1; //拉高输入 #60 //60个时间单位后 in <= 1'b0; //拉低输入,制造第2个下降沿 end //always代表重复进行,#10代表每10个时间单位 //每10个时间单位反转时钟,即时钟周期为20个时间单位(20ns) always #10 sys_clk = ~sys_clk; //例化被测试模块 detect_1 detect_1_inst ( .sys_clk (sys_clk ), .sys_rst_n (sys_rst_n ), .in (in), .in_neg (in_neg) ); endmodule 2.5、仿真结果 使用ModelSim执行仿真,仿真出来的波形如所示: 图片 从波形图可以看到: 10ns后停止复位 在第1条参考线处输入信号 in 产生了第1个下降沿信号 在第3条参考线处输入信号 in 产生了第2个下降沿信号 在第1条参考线和第2条参考线之间的产生了一个周期的下降沿指示信号 in_neg 在第3条参考线和第4条参考线之间的产生了一个周期的下降沿指示信号 in_neg 3、采用2级触发器的边沿检测电路(以下降沿为例) 3.1、设计方法 设计波形图如下所示: 图片 各信号说明如下: sys_clk:基准时钟信号(这里设定为50MHz,周期20ns) sys_rst_n:低电平有效的复位信号 in:输入信号,需要对其进行下降沿检测 in_d1:对输入信号寄存1拍 in_d2:对输入信号寄存2拍 ~in_d1:in_d1信号的反相信号 in_neg:得到的下降沿指示信号,该信号为 ~ind1 && ind2 对上图进行分析: 信号in是我们需要对其进行下降沿检测的信号 信号in_d1是使用寄存器寄存in信号,即对其打1拍 信号in_d2是使用寄存器寄存in_d1信号,即对其打1拍 信号~in_d1是将信号in_d1反向 输入信号开始为高电平,在L2处变为低电平,产生第1个下降沿,在L5出产生第2个下降沿 A处为产生的第1个下降沿指示信号,B处为产生的第2个下降沿指示信号 输出的下降沿指示信号落后下降沿一个时钟周期,这是因为对输入信号进行了寄存以消除亚稳态 由此我们可以推导出边沿检测信号产生的一般方法: 将需要检测的信号分别寄存1拍、2拍,同步到系统时钟域下,得到信号 in_d1、in_d2 将in_d1信号反向,得到信号 ~in_d1 将in_d2信号反向,得到信号 ~in_d2 通过组合逻辑电路可以得到下降沿信号 in_neg:assign in_neg = ~in_d1 && in_d2 同样通过组合逻辑电路可以得到上升沿信号 in_pos:assign in_pos = in_d1 && ~in_d2 双边沿检测就是将上两条加(或运算)起来就可以了,化简后有:双边沿信号 in_both = in_d1 ^ in_d2 3.2、Verilog实现 根据上文分析不难编写Verilog代码如下: //使用1级寄存器的下降沿检测电路 module detect_2 ( input sys_clk, //时钟(设定为 50MHz) input sys_rst_n, //复位信号(n 表示低电平有效) input in, //需要进行下降沿检测的输入信号 output in_neg //输出的下降沿指示信号 ); //reg 定义 reg in_d1; //寄存1拍的信号 reg in_d2; //寄存2拍的信号 assign in_neg = ~in_d1 && in_d2;//组合逻辑得到下降沿 //上升沿: assign in_pos = in && ~in_d1; //双边沿: assign in_pos = in ^ in_d1; //寄存模块,将输入信号打1拍、打2拍 always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n)begin in_d1 <= 1'b0; //复位清零 in_d2 <= 1'b0; end else begin in_d1 <= in; //寄存1拍 in_d2 <= in_d1; //寄存2拍 end end endmodule3.3、RTL电路 图片 上图为生成的RTL电路:该电路由2级D触发器+与逻辑门构成。 3.4、Testbench Testbench文件同2.4章。 3.5、仿真结果 使用ModelSim执行仿真,仿真出来的波形如所示: 图片 从波形图可以看到: 10ns后停止复位 在第1条参考线处输入信号 in 产生了第1个下降沿信号 在第4条参考线处输入信号 in 产生了第2个下降沿信号 在第2条参考线和第3条参考线之间的产生了一个周期的下降沿指示信号 in_neg 在第5条参考线和第6条参考线之间的产生了一个周期的下降沿指示信号 in_neg 两级寄存器构成的边沿检测电路可以有效的防止亚稳态的产生,产生的使能信号会落后一个时钟周期。 4、参考文献 【从零开始走进FPGA】你想干嘛——边沿检测技术 FPGA的边沿检测 FPGA基础入门篇(四) 边沿检测电路
FPGA&ASIC
刘航宇
4年前
0
2,507
1
上一页
1
...
3
4
5
...
13
下一页