首页
📁归档
⏳时光机
🚩友链
📫留言
📧订阅本站
推荐
📕考研课程
🏜️ 免费壁纸
❤ 捐助本站
💰资助名单
🎵音乐实验
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
20,027 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
10,581 阅读
3
【高数】形心计算公式讲解大全
9,022 阅读
4
Vivado-FPGA Verilog烧写固化教程
7,841 阅读
5
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
7,795 阅读
🪶微语&随笔
励志美文
我的随笔
写作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
/
注册
ASIC/FPGA(共31篇)
找到
31
篇与
ASIC/FPGA
相关的结果
- 第 3 页
【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,502
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 casebib_ibi 3'b000 : booth_o <= 0; 3'b001 : booth_o <= { a_ilength−1length-1length−1, a_i}; 3'b010 : booth_o <= { a_ilength−1length-1length−1, a_i}; 3'b011 : booth_o <= a_i<<1; 3'b100 : booth_o <= -ai<<1a_i<<1ai<<1; 3'b101 : booth_o <= -{a_ilength−1length-1length−1,a_i}; 3'b110 : booth_o <= -{a_ilength−1length-1length−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、仿真分析 前端逻辑仿真 本设计是单纯的组合逻辑,由仿真结果可知有符号乘法设计结果完全正确。 图片 电路与性能仿真 电路图 图片 面积报告 DC综合后,总共的单元面积为7853.630484等效门,总面积为78273.983938等效门。 图片 功耗报告 图片 由于该电路是完全的组合逻辑,无CLK端口,因此未作时序约束。 后端 做到这里完成了前端中端设计任务,在流片前还需要完成后端设计及验证。由于本电路规模大,我们可以利用EDA如(SOCE或者ICC)完成版图布局,由于时间仓促,笔者暂未更新后端教程。 代码下载
FPGA&ASIC
VLSI&IC验证
# ASIC/FPGA
刘航宇
4年前
0
1,510
4
2022-10-16
UART串行接口设计及通信实现
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种异步收发传输器。 图片 异步串行、接口原理分析 串行通信电气特性 通常,UART串行接口采用RS-232-C电气标准进行通信。RS-232-C是美国电子工业协会EIA(Electronic Industry Association)制定的一种串行物理接口标准。对于一般的通信,不需要掌握RS-232-C的所有信号的定义,只需要使用其中的2个信号TXD(数据发送)和RXD(数据接收),另外,参加通信的设备要将GND(地)要连到一起(共地)。 数据线TxD和RxD的电平标准为: 逻辑1=-3V~-15V;逻辑0=+3~+15V RS-232-C标准规定的数据传输速率为9600、19200、115200等波特率。波特率是衡量资料传送速率的指标,表示每秒钟传送的符号数。如果波特率为9600,那么发送和接收数据都应该为9600bits/s。本节采用9600bit/s进行通信。 图片 UART串行数据格式 起始位(逻辑0 )+数据位(本节为8位数据位)+奇偶校验位(本节为偶校验)+停止位(本节使用1位停止位)+空闲位(逻辑1) 图片 通信的安全性问题 串行异步通信,发送方和接收方使用各自的时钟。例如从FPGA电路板和计算机之间的通信,因此,不能保证两者的时钟完全相同。 为保证接收数据的准确性,因此需要用较高的采样率进行采样,本节采用16倍的采样率进行采样,简单的选取中间采样点的值作为接收的数据。如图采用16倍波特率的采样率执行采样,一个比特可采集16个点,选取中间点即从第一个采样点开始的第8或第9个采样点(笑脸附近上升沿)采集该位数据,在接收双方始终频率相差不大的情况下,能够正确的完成通信。 图片 功能设计和演示 预计实现的功能(演示): 将拨码开关代表的数据发送到串口(8位); 从串口接收电脑发来的数据,以数码管显示(8位)。 主模块设计 ⚫ 调用分频模块 ⚫ 调用数码管显示IP ⚫ 调用按键消抖动IP ⚫ 调用串口发送模块 ⚫ 调用串口接收模块 module v1( input clk, input [11:0] sw, input [3:0] btn,//行列键盘的输入 output [3:0] row,//行列键盘的行输出 output [7:0] seg, output [5:0] an, output [11:0] led, output txd, input rxd ); wire clk_ms,clk_20ms,clk_16x,clk_x; wire [3:0] btnout; wire [23:0] data_disp; wire data_ready; wire data_error; assign row[3:0]=1;//输出全部为高电平, 本例程只用4个 divclk my_divclk(.clk(clk),.clk_ms(clk_ms),.btnclk(clk_20ms),.clk_16x(clk_16x),.clk_x(clk_x)); //调用分频模块 ip_disp_0 uut_disp(//调用显示IP .clk(clk), .rst(0), .dispdata({data_disp[11:0],sw[11:0]}), .seg(seg), .an(an) ); ip_ajxd_0 uut_ajxd(//调用按键消抖动IP .btn_clk(clk_20ms), .btn_in(btn), .btn_out(btnout) ); uart_tx (.clk_x(clk_x),.data_in(sw[7:0]),.btn(btnout),.txd(txd),.led(led)); //调用串口发送模 块 uart_rx(.clk_16x(clk_16x),.rst(btnout[0]),.rxd(rxd),.data_disp(data_disp),.data_ready(data_rea dy),.data_error(data_error));//调用串口发送模块 endmodule图片 串口发送程序设计 图片 串口接收程序设计 图片 图片
嵌入式&系统
通信&信息处理
# ASIC/FPGA
刘航宇
4年前
0
802
0
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
912
2
2022-07-06
【IC设计】数字IC设计流程及学习指南
IC的设计过程可分为两个部分,前端设计(也称逻辑设计)和后端设计(也称物理设计)并没有统一严格的界限,涉及到与工艺有关的设计就是后端设计。 目录 数字IC设计流程1. 规格制定 2. 详细设计 3. HDL编码 4. 仿真验证 5. 逻辑综合――Design Compiler5.1. STA 5.2. 形式验证 技能图解 1.语言 2.基础知识 3.设计工具 4.相关岗位 数字IC设计流程 1. 规格制定 芯片规格,也就像功能列表一样,是客户向芯片设计公司(称为Fabless,无晶圆设计公司)提出的设计要求,包括芯片需要达到的具体功能和性能方面的要求。 2. 详细设计 Fabless根据客户提出的规格要求,拿出设计解决方案和具体实现架构,划分模块功能。目前架构的验证一般基于SystemC语言,对构架模型的仿真可以使用SystemC的仿真工具。其中典型的例子是Synopsys公司的CoCentric和Summit公司的Visual Elite等。 3. HDL编码 使用硬件描述语言(VHDL,Verilog HDL,业界公司一般都是使用后者)将模块功能以代码来描述实现,也就是将实际的硬件电路功能通过HDL语言描述出来,形成RTL(寄存器传输级)代码。 设计输入工具:具有强大的文本编辑功能,多种输入方法(VHDL,Verilog,状态转移图,模块图等),语法模板,语法检查,自动生产代码和文档等功能。如Active-HDL,VisualVHDL/Verilog等。 RTL分析检查工具:Synopsys LEDA 4. 仿真验证 仿真验证就是检验编码设计的正确性,检验的标准就是第一步制定的规格。看设计是否精确地满足了规格中的所有要求。规格是设计正确与否的黄金标准,一切违反,不符合规格要求的,就需要重新修改设计和编码。 设计和仿真验证是反复迭代的过程,直到验证结果显示完全符合规格标准。 仿真验证工具Synopsys的VCS,Mentor ModelSim,Cadence Verilog-XL,Cadence NC-Verilog。 5. 逻辑综合――Design Compiler 仿真验证通过,进行逻辑综合。逻辑综合的结果就是把设计实现的HDL代码翻译成门级网表netlist。综合需要设定约束条件,就是你希望综合出来的电路在面积,时序等目标参数上达到的标准。逻辑综合需要基于特定的综合库,不同的库中,门电路基本标准单元(standard cell)的面积,时序参数是不一样的。所以,选用的综合库不一样,综合出来的电路在时序,面积上是有差异的。 一般来说,综合完成后需要再次做仿真验证(这个也称为后仿真,之前的称为前仿真)逻辑综合工具Synopsys的Design Compiler(DC),Cadence的 PKS,Synplicity的Synplify等。另外,和综合工具配合使用的还有很多其他工具,如静态时间分析工具,等效性检查工具等等。Synopsys公司和Cadence公司都提供完整的工具包。 5.1. STA Static Timing Analysis(STA),静态时序分析,这也属于验证范畴,它主要是在时序上对电路进行验证,检查电路是否存在建立时间(setup time)和保持时间(hold time)的违例(violation)。这个是数字电路基础知识,一个寄存器出现这两个时序违例时,是没有办法正确采样数据和输出数据的,所以以寄存器为基础的数字芯片功能肯定会出现问题。STA工具有Synopsys的Prime Time。 5.2. 形式验证 这也是验证范畴,它是从功能上(STA是时序上)对综合后的网表进行验证。常用的就是等价性检查(Equivalence Check)方法,以功能验证后的HDL设计为参考,对比综合后的网表功能,他们是否在功能上存在等价性。这样做是为了保证在逻辑综合过程中没有改变原先HDL描述的电路功能。形式验证工具有Synopsys的Formality。前端设计的流程暂时写到这里。从设计程度上来讲,前端设计的结果就是得到了芯片的门级网表电路。 技能图解 图片 1.语言 主流的Verilog、VHDL Verilog语言与软件语言最大的区别就是,因为它是用于描述电路的,因此它的写法是非常固定的,因为电路的变化是非常有限的。学习Verilog的时候,很多时候我们并不是在学习这门语言本身,而是学习其对应的电路特征,以及如何对这个电路进行描述。如果心中没有电路,那么你是不可能写好Verilog的。 VHDL语言的严谨性比Verilog要好,不像Verilog中一样存在大量符合语法却永远无法综合的语句。 SpinalHDL、Myhdl等 SpinalHDL和Chisel师承一脉,都是基于Scala来进行电路描述。其本质上仍旧是HDL而非HLS,在设计之前依旧需要有清晰的电路结构,只不过是在电路描述上更加方便和快捷而已。 目前有一些公司已经应用于FPGA设计中,ASIC应用还有待探究。 SpinalHDL资料: https://spinalhdl.github.io/SpinalDoc-RTD/master/index.html SystemVerilog/SystemC : 这两种语言都是为了验证而存在的,掌握基本的验证方法学有助于提高自己的debug效率,SystemVerilog是一种面向对象的语言,其设计的本意是用于搭建验证平台,主流的UVM方法也都是基于SystemVerilog实现的,所以立志成为IC验证工程师的同学,SystemVerilog的深入学习和流行方法论的学习都是必不可少的。 而对于那些只想做IC设计的同学而言,SystemVerilog同样也是值得学习的。且不说本文前面提到的用于提高验证效率的debug方法,即使只是为了做好设计,SystemVerilog也是大有用武之地。很多顶级的IC设计公司内部都开始使用SystemVerilog进行RTL设计了。 由于在SystemVerilog中加入了很多类似always_ff、always_comb等用于显式表明综合电路意图的新语法,代码的可读性更高,综合过程中也减少了歧义,尽可能地保证了综合结果与设计意图的一致性。从另一个角度来说,assertion的加入也极大地提高了代码的debug效率,非常有助于在大规模的数据交互过程中定位到出错的初始点,没有掌握的同学可以多花一些时间学习一下。 -> SV基础知识 脚本语言:Makefile/Perl/Python/Shell Makefile/Perl/Python/Shell都是常用的脚本语言,进行批量修改文本内容,实现自动化操作等,掌握一门得心应手的脚本语言将对工作效率的提升帮助极大。但凡做过一次的事情,就没有必要重复第二次。 -> Perl在IC中的应用 脚本语言:Tcl 在IC设计这个领域中,Tcl是一门非常常见的语言。可以用于描述时序和管脚约束文件,UPF信息,也可以用来搭建简单的工作平台。既是很多EDA工具默认支持的脚本语言,也是这些工具配置和输出的文件格式。因此,能够读懂Tcl,掌握Tcl语言的基本语法,就可以帮助更好的使用EDA工具。 -> 从零开始学Tcl C语言 作为基础语言,在验证时,往往需要用到C写的case; 2.基础知识 数电模电、电路分析、数据结构、计算机体系架构等 AMBA总线 AXI、AHB、APB、CHI、ACE等; -> AMBA总线 高低速接口 PCIE、DMA、DDR、USB、UART、SPI、I2C、MIPI等 -> 总线接口 低功耗设计 动态功耗、静态功耗、常见低功耗设计方法 -> 低功耗设计 静态时序分析 建立时间、保持时间、亚稳态及其解决方法、时序约束 -> 时序约束策略 、亚稳态、 FPGA中的亚稳态 设计方法 乒乓操作、流水线、串并转换、无毛刺切换、状态机等 跨时钟处理:快到慢、慢到快、异步FIFO(FIFO深度计算)等 ->时钟切换电路(Glitch-free clock switching circuit)、异步FIFO、同步FIFO、同步和异步FIFO、FIFO深度计算 3.设计工具 linux系统:linux、shell常见命令 gvim/emac编辑器: 熟练使用Vim/Emacs,用编程的方式来编程,可以提高效率。 版本管理工具:SVN、Git、p4等 版本管理,简而言之,就是一种用于记录和查询文件版本改动的工具; EDA工具 仿真工具:NCVerilog/ VCS/ ModelSim/ iVerilog 以上是比较业界比较主流的仿真工具,其中Icarus Verilog (iVerilog)为开源工具,仿真过程需要了解: 如何指定编译的文件类型; 如何指定编译文件清单; 如何指定索引目录; 如何指定仿真精度; 如何指定临时的宏变量; 如何指定语法检查的严苛等级; 如何混合编译由多种语言写成的工程; 如何调用不同波形生成工具的pli接口; 如何配合SDF反标进行后仿等; -> 芯片后仿及SDF反标、VCS实用技巧、开源verilog仿真工具iverilog+GTKWave 波形查看工具:DVE/ Verdi/ gtkWave 以上是业界比较主流的波形查看工具,所有的波形查看器都必须支持标准波形文件.vcd格式,但是由于.vcd格式的存储性能并不好,冗余信息过多,所以各波形查看工具都纷纷推出了自己支持的波形文件格式,如DVE的.vpd,Verdi的.fsdb,ModelSim的*.wlf; gtkWave也是跨平台的,而且简单易用,支持.vcd标准格式,同时支持高性能压缩格式.lxt和*.fst,gtkWave自带vcd转fst的转换器; 通常几G左右的.vcd格式波形转换为.vpd格式后只有几十MB左右,而转换为.fsdb后通常会更小,因此将标准波形文件.vcd转换为其他压缩格式更加有利于数据备份。 -> fsdb实用技巧、verdi实用技巧 逻辑等效性检查工具:formality-> 形式验证与formality基本流程 从功能上对综合后的网表进行验证,常用的就是等价性检查方法,以功能验证后的HDL设计为参考,对比综合后的网表功能,他们是否在功能上存在等价性。这样做是为了保证在逻辑综合过程中没有改变原先HDL描述的电路功能。 综合工具:dc ->DC综合 逻辑综合就是将HDL代码翻译成门级网表netlist; lint/cdc检查工具:spyglass -> SpyGlass安装教程、spyglass 基础操作、spyglass 常见错误lint 后端:PT、IIC2、innovus等 FPGA综合布线布局工具:synplify/vivado-> ->synplify 基础操作、基于synplify+vivado生成bitfile 其他:VC_static_tools ->VC_static Tools安装教程 4.相关岗位 前端设计 RTL IP设计:从算法协议到RTL coding的能力; SoC设计集成/验证:总线、功耗、系统架构的能力; SOC侧重将IP,模块集成形成一个系统,所以片上互联总线和外设接口是掌握的重点。片内互联总线一般是AMBA总线。外设接口又分为低速接口和高速接口,其中低速接口包括I2C,UART,SPI等,高速接口包括SATA,USB,PCIE,DDR等; 验证 FPGA原型验证:HAPS -> HAPS®-100原型系统、芯片验证技术 |HAPS与高性能ASIC原型验证 UVM验证:SV、UVM主流验证方法学 ->UVM芯片验证 EMU验证:zebu、帕拉丁 -> 芯片验证应用|基于ZeBu平台的虚拟主机应用方案 综合: 逻辑综合就是将HDL代码翻译成门级网表netlist; STA Static Timing Analysis(STA),静态时序分析,它主要是在时序上对电路进行验证,检查电路是否存在建立时间(setup time)和保持时间(hold time)的违例(violation),一个寄存器出现这两个时序违例时,是没有办法正确采样数据和输出数据的,所以以寄存器为基础的数字芯片功能肯定会出现问题。 DFT Design ForTest,可测性设计。芯片内部往往都自带测试电路,DFT的目的就是在设计的时候就考虑将来的测试。DFT的常见方法就是,在设计中插入扫描链,将非扫描单元(如寄存器)变为扫描单元;如bist、Scan Design、JTAG、ATPG等; 后端 物理验证、布线布局、静态时序分析、等价性检查、功耗分析
FPGA&ASIC
# ASIC/FPGA
刘航宇
4年前
0
3,087
9
【9】Verilog练习-串口指令处理器
目录 理论: 代码cmd_pro UART_top 现象 理论: 图片 图片 图片 代码 RX、TX需要与之在相同目录下 cmd_pro //指令处理器 `timescale 1ns/10ps module cmd_pro( clk, res, din_pro, en_din_pro, dout_pro, en_dout_pro, rdy ); input clk; input res; input[7:0] din_pro;//指令和数据输入端口; input en_din_pro;//输入使能; output[7:0] dout_pro;//指令执行结果; output en_dout_pro;//指令输出使能; output rdy;//串口发送模块空闲标志,0表示空闲; parameter add_ab=8'h0a; parameter sub_ab=8'h0b; parameter and_ab=8'h0c; parameter or_ab=8'h0d; reg[2:0] state;//主状态机寄存器(2^3个状态); reg[7:0] cmd_reg,A_reg,B_reg;//存放指令、A和B; reg[7:0] dout_pro; reg en_dout_pro; always@(posedge clk or negedge res) if(~res)begin state<=0;cmd_reg<=0;A_reg<=0;B_reg<=0;dout_pro<=0; en_dout_pro<=0; end else begin case(state) 0://等指令; begin en_dout_pro<=0; if(en_din_pro) begin cmd_reg<=din_pro; state<=1; end end 1://收A; begin if(en_din_pro) begin A_reg<=din_pro; state<=2; end end 2://收B; begin if(en_din_pro) begin B_reg<=din_pro; state<=3; end end 3://指令译码和执行 begin state<=4; case(cmd_reg) add_ab: begin dout_pro<=A_reg+B_reg; end sub_ab: begin dout_pro<=A_reg-B_reg; end and_ab: begin dout_pro<=A_reg&B_reg; end or_ab: begin dout_pro<=A_reg|B_reg; end endcase end 4://发送指令执行结果 begin if(~rdy)begin en_dout_pro<=1; state<=0; end end default:// begin state<=0; en_dout_pro<=0; end endcase end endmoduleUART_top //串口指令处理器 `timescale 1ns/10ps module UART_top( clk, res, RX, TX ); input clk; input res; input RX; output TX; wire[7:0] din_pro; wire en_din_pro; wire[7:0] dout_pro; wire en_dout_pro; wire rdy; UART_Rxer UART_Rxer( .clk(clk), .res(res), .RX(RX), .data_out(din_pro), .en_data_out(en_din_pro) ); UART_TXer UART_TXer( .clk(clk), .res(res), .data_in(dout_pro), .en_data_in(en_dout_pro), .TX(TX), .rdy(rdy) ); cmd_pro cmd_pro( .clk(clk), .res(res), .din_pro(din_pro), .en_din_pro(en_din_pro), .dout_pro(dout_pro), .en_dout_pro(en_dout_pro), .rdy(rdy) ); endmodule //-----testbench of UART_top----- module UART_top_tb; reg clk,res; wire RX; wire TX; reg[45:0] RX_send;//里面装有串口字节发送数据 assign RX=RX_send[0];//连接RX; reg[12:0] con; UART_top UART_top( clk, res, RX, TX ); initial begin clk<=0;res<=0;RX_send<={1'b1,8'h09,1'b0,1'b1,8'h06,1'b0,1'b1,8'h0a,1'b0,16'hffff}; con<=0; #17 res<=1; #4000000 $stop; end always #5 clk<=~clk; always@(posedge clk)begin if(con==5000-1)begin con<=0; end else begin con<=con+1; end if(con==0)begin RX_send[44:0]<=RX_send[45:1]; RX_send[45]<=RX_send[0]; end end endmodule现象 图片
FPGA&ASIC
# ASIC/FPGA
刘航宇
4年前
0
410
2
2022-06-21
【8】Verilog练习-串口数据发送
理论: 图片 图片 代码: //串口发送模块 `timescale 1ns/10ps module UART_TXer( clk, res, data_in, en_data_in, TX, rdy ); input clk; input res; input[7:0] data_in;//准备发送的数据; input en_data_in;//发送使能; output TX;//输出使能 output rdy;//空闲标志,0表示空闲; reg[3:0] state;//主状态机寄存器; reg[9:0] send_buf;//发送寄存器 assign TX=send_buf[0];//连接TX; reg[9:0] send_flag;//用于判断右移结束; reg[12:0] con;//用于计算波特周期 reg rdy; always@(posedge clk or negedge res) if(~res)begin state<=0;send_buf<=1;con<=0;send_flag<=10'b10_0000_0000;rdy<=0; end else begin case(state) 0://等待使能信号; begin if(en_data_in)begin send_buf={1'b1,data_in,1'b0}; send_flag<=10'b10_0000_0000; rdy<=1; state<=1; end end 1://串口发送,寄存器右移; begin if(con==5000-1)begin con<=0; end else begin con<=con+1; end if(con==5000-1)begin send_buf[8:0]<=send_buf[9:1]; send_flag[8:0]<=send_flag[9:1]; end if(send_flag[0])begin rdy<=0; state<=0; end end endcase end endmodule //----testbench of UART_TXer--- module UART_TXer_tb; reg clk,res; reg[7:0] data_in; reg en_data_in; wire TX; wire rdy; UART_TXer UART_TXer( //同名例化; clk, res, data_in, en_data_in, TX, rdy ); initial begin clk<=0;res<=0;data_in<=8'h7f;en_data_in<=0; #17 res<=1; #30 en_data_in<=1; #10 en_data_in<=0; #10000 $stop; end always #5 clk<=~clk; endmodule结果: 图片
FPGA&ASIC
# ASIC/FPGA
刘航宇
4年前
0
490
3
上一页
1
2
3