主题资源网站建设 模块五作业,天津网站建设服务公司,织梦网站数据库库直接上传的 没有后台备份 需要怎么还原,网站制作的趋势强烈建议用同步设计2.在设计时总是记住时序问题3.在一个设计开始就要考虑到地电平或高电平复位、同步或异步复位、上升沿或下降沿触发等问题#xff0c;在所有模块中都要遵守它4.在不同的情况下用if和case#xff0c;最好少用if的多层嵌套#xff08;1层或2层比较合适#…强烈建议用同步设计2.在设计时总是记住时序问题3.在一个设计开始就要考虑到地电平或高电平复位、同步或异步复位、上升沿或下降沿触发等问题在所有模块中都要遵守它4.在不同的情况下用if和case最好少用if的多层嵌套1层或2层比较合适当在3层以上时最好修改写法因为这样不仅可以reduce area而且可以获得好的timing5.在锁存一个信号或总线时要小心对于整个design尽量避免使用latch因为在DFT时很难test。6.确信所有的信号被复位在DFT时所有的FlipFlop都是controllable7.永远不要再写入之前读取任何内部存储器如SRAM8.从一个时钟到另一个不同的时钟传输数据时用数据缓冲他工作像一个双时钟FIFO是异步的可以用Async SRAM搭建Async FIFO。9.在VHDL中二维数组可以使用它是非常有用的。在VERILOG中他仅仅可以使用在测试模块中不能被综合10.遵守register-in register-out规则11.像synopsys的DC的综合工具是非常稳定的任何bugs都不会从综合工具中产生12.确保FPGA版本与ASIC的版本尽可能的相似特别是SRAM类型若版本一致是最理想的但是在工作中FPGA版本一般用FPGA自带的SRAMASIC版本一般用厂商提供的SRAM。13.在嵌入式存储器中使用BIST14.虚单元和一些修正电路是必需的15.一些简单的测试电路也是需要的经常在一个芯片中有许多测试模块16.除非低功耗不要用门控时钟强烈建议不要在design中使用gate clock17.不要依靠脚本来保证设计。但是在脚本中的一些好的约束能够起到更好的性能例如前向加法器18.如果时间充裕通过时钟做一个多锁存器来取代用MUX19.不要用内部tri-state, ASIC需要总线保持器来处理内部tri-state如IO cell。20.在top level中作pad insertion21.选择pad时要小心如上拉能力施密特触发器5伏耐压等选择合适的IO cell22.小心由时钟偏差引起的问题23.不要试着产生半周期信号24.如果有很多函数要修正请一个一个地作修正一个函数检查一个函数25.在一个计算等式中排列每个信号的位数是一个好习惯即使综合工具能做26.不要使用HDL提供的除法器27.削减不必要的时钟。它会在设计和布局中引起很多麻烦大多数FPGA有14个专门的时钟通道良好代码编写风格可以满足信、达、雅的要求。在满足功能和性能目标的前提下增强代码的可读性、可移植性首要的工作是在项目开发之前为整个设计团队建立一个命名约定和缩略语清单以文档的形式记录下来并要求每位设计人员在代码编写过程中都要严格遵守。良好代码编写风格的通则概括如下 1 对所有的信号名、变量名和端口名都用小写这样做是为了和业界的习惯保持一致对常量名和用户定义的类型用大写 2 使用有意义的信号名、端口名、函数名和参数名 3 信号名长度不要太长 4 对于时钟信号使用clk 作为信号名如果设计中存在多个时钟使用clk 作为时钟信号的前缀 5 对来自同一驱动源的信号在不同的子模块中采用相同的名字这要求在芯片总体设计时就定义好顶层子模块间连线的名字端口和连接端口的信号尽可能采用相同的名字 6 对于低电平有效的信号应该以一个下划线跟一个小写字母b 或n 表示。注意在同一个设计中要使用同一个小写字母表示低电平有效 7 对于复位信号使用rst 作为信号名如果复位信号是低电平有效建议使用rst_n 8 当描述多比特总线时使用一致的定义顺序对于verilog 建议采用bus_signal[x:0]的表示 9 尽量遵循业界已经习惯的一些约定。如*_r 表示寄存器输出*_a 表示异步信号*_pn 表示多周期路径第n 个周期使用的信号*_nxt 表示锁存前的信号*_z 表示三态信号等 10在源文件、批处理文件的开始应该包含一个文件头、文件头一般包含的内容如下例所示文件名作者模块的实现功能概述和关键特性描述文件创建和修改的记录包括修改时间修改的内容等 11使用适当的注释来解释所有的always 进程、函数、端口定义、信号含义、变量含义或信号组、变量组的意义等。注释应该放在它所注释的代码附近要求简明扼要只要足够说明设计意图即可避免过于复杂 12每一行语句独立成行。尽管VHDL 和Verilog 都允许一行可以写多个语句当时每个语句独立成行可以增加可读性和可维护性。同时保持每行小于或等于72 个字符这样做都是为了提高代码得可读性 13建议采用缩进提高续行和嵌套语句得可读性。缩进一般采用两个空格如西安交通大学SOC 设计中心2 如果空格太多则在深层嵌套时限制行长。同时缩进避免使用TAB 键这样可以避免不同机器TAB 键得设置不同限制代码得可移植能力 14在RTL 源码的设计中任何元素包括端口、信号、变量、函数、任务、模块等的命名都不能取Verilog 和VHDL 语言的关键字 15在进行模块的端口申明时每行只申明一个端口并建议采用以下顺序 输入信号的clk、rst、enables other control signals、data and address signals。然后再申明输出信号的clk、rst、enalbes other control signals、data signals 16在例化模块时使用名字相关的显式映射而不要采用位置相关的映射这样可以提高代码的可读性和方便debug 连线错误 17如果同一段代码需要重复多次尽可能使用函数如果有可能可以将函数通用化以使得它可以复用。注意内部函数的定义一般要添加注释这样可以提高代码的可读性 18尽可能使用循环语句和寄存器组来提高源代码的可读性这样可以有效地减少代码行数 19对一些重要的always 语句块定义一个有意义的标号这样有助于调试。注意标号名不要与信号名、变量名重复 20代码编写时的数据类型只使用IEEE 定义的标准类型在VHDL 语言中设计者可以定义新的类型和子类型但是所有这些都必须基于IEEE 的标准 21在设计中不要直接使用数字作为例外可以使用0 和1。建议采用参数定义代替直接的数字。同时在定义常量时如果一个常量依赖于另一个常量建议在定义该常量时用表达式表示出这种关系 22不要在源代码中使用嵌入式的dc_shell 综合命令。这是因为其他的综合工具并不认得这些隐含命令从而导致错误的或较差的综合结果。即使使用Design Compiler当综合策略改变时嵌入式的综合命令也不如放到批处理综合文件中易于维护。这个规则有一个例外的综合命令即编译开关的打开和关闭可以嵌入到代码中 23在设计中避免实例化具体的门级电路。门级电路可读性差且难于理解和维护如果使用特定工艺的门电路设计将变得不可移植。如果必须实例化门电路我们建议采用独立于工艺库的门电路如SYNOPSYS 公司提供的GTECH 库包含了高质量的常用的门级电路 24避免冗长的逻辑和子表达式 25避免采用内部三态电路建议用多路选择电路代替内部三态电路。 规则 #1: 建立时序逻辑模型时采用非阻塞赋值语句。zCq,s[1]F4RE0规则 #2: 建立latch模型时采用非阻塞赋值语句。EDA中国门户网站1X!y4m]0r O B规则 #3: 在always块中建立组合逻辑模型时采用阻塞赋值语句。2n$DW8?$6W0规则 #4: 在一个always块中同时有组合和时序逻辑时时采用非阻塞赋值语句。rz T2x4pn4U 0规则 #5: 不要在一个always块中同时采用阻塞和非阻塞赋值语句。^p/ulTOB0规则 #6: 同一个变量不要在多个always块中赋值。EDA中国门户网站8q%UOC%yX b:Ma规则 #7: 调用$strobe系统函数显示用非阻塞赋值语句赋的值。EDA中国门户网站$U]wg4w-FE5a规则 #8: 不要使用#0延时赋值。
组合逻辑1敏感变量的描述完备性Verilog中用always块设计组合逻辑电路时在赋值表达式右端参与赋值的所有信号都必须在always (敏感电平列表)中列出,always中if语句的判断表达式必须在敏感电平列表中列出。如果在赋值表达式右端引用了敏感电平列表中没有列出的信号在综合时将会为没有列出的信号隐含地产生一个透明锁存器。这是因为该信号的变化不会立刻引起所赋值的变化而必须等到敏感电平列表中的某一个信号变化时它的作用才表现出来即相当于存在一个透明锁存器把该信号的变化暂存起来待敏感电平列表中的某一个信号变化时再起作用纯组合逻辑电路不可能作到这一点。综合器会发出警告。Example1: input a,b,c; reg e,d; always (a or b or c) begin edab; /*d没有在敏感电平列表中,d变化时e不会立刻变化,直到a,b,c中某一个变化*/ de |c; end Example2: input a,b,c; reg e,d; always (a or b or c or d) begin edab; /*d在敏感电平列表中,d变化时e立刻变化*/ de |c; end 2,条件的描述完备性如果if语句和case语句的条件描述不完备也会造成不必要的锁存器。Example1: if (a1b1) q1b1;//如果a1b0,q? q将保持原值不变生成锁存器Example2: if (a1b1) q1b1; else q1b0;//q有明确的值。不会生成锁存器Example3: reg[1:0] a,q; .... case (a) 2b00 : q2b00; 2b01 : q2b11;//如果a2b10或a2b11,q? q将保持原值不变锁存器 endcase Example4: reg[1:0] a,q; .... case (a) 2b00 : q2b00; 2b01 : q2b11; default: q2b00;//q有明确的值。不会生成锁存器 endcase Verilog中端口的描述1端口的位宽最好定义在I/O说明中,不要放在数据类型定义中Example1: module test(addr,read,write,datain,dataout) input[7:0] datain; input[15:0] addr; input read,write; output[7:0] dataout; //要这样定义端口的位宽wire addr,read,write,datain; reg dataout; Example2: module test(addr,read,write,datain,dataout) input datain,addr,read,write; output dataout; wire[15:0] addr; wire[7:0] datain; wire read,write; reg[7:0] dataout; //不要这样定义端口的位宽2端口的I/O与数据类型的关系 端口的I/O 端口的数据类型 module内部 module外部 input wire wire或reg output wire或reg wire inout wire wire 3assign语句的左端变量必须是wire直接用给变量赋值时左端变量必须是regExample: assign ab; //a必须被定义为wire******** begin ab; //a必须被定义为regend VHDL中STD_LOGIC_VECTOR和INTEGER的区别例如A是INTEGER型范围从0到255B是STD_LOGIC_VECTOR定义为8位。A累加到255时再加1就一直保持255不变不会自动反转到0除非令其为0而B累加到255时再加1就会自动反转到0。所以在使用时要特别注意以触发器为例说明描述的规范性1无置位/清零的时序逻辑 always ( posedge CLK) begin QD; end 2有异步置位/清零的时序逻辑 异步置位/清零是与时钟无关的当异步置位/清零信号到来时触发器的输出立即 被置为1或0不需要等到时钟沿到来才置位/清零。所以必须要把置位/清零信号 列入always块的事件控制表达式。 always ( posedge CLK or negedge RESET) begin if (!RESET) Q0; else QD; end 3有同步置位/清零的时序逻辑 同步置位/清零是指只有在时钟的有效跳变时刻置位/清零才能使触发器的输出分 别转换为1或0。所以不要把置位/清零信号列入always块的事件控制表达式。但是 必须在always块中首先检查置位/清零信号的电平。 always ( posedge CLK ) begin if (!RESET) Q0; else QD; end 结构规范性 在整个芯片设计项目中行为设计和结构设计的编码是最重要的一个步骤。 它对逻辑综合和布线结果、时序测定、校验能力、测试能力甚至产品支持 都有重要的影响。考虑到仿真器和真实的逻辑电路之间的差异为了有效的 进行仿真测试 1避免使用内部生成的时钟 内部生成的时钟称为门生时钟gated clock。如果外部输入时钟和门生时钟同时驱动 则不可避免的两者的步调不一致造成逻辑混乱。而且门生时钟将会增加测试的难度 和时间。 2绝对避免使用内部生成的异步置位/清零信号 内部生成的置位/清零信号会引起测试问题。使某些输出信号被置位或清零无法正常 测试。3避免使用锁存器 锁存器可能引起测试问题。对于测试向量自动生成ATPG 为了使扫描进行锁存器需要置为透明模式transparent mode 反过来测试锁存器需要构造特定的向量这可非同一般。 4时序过程要有明确的复位值 使触发器带有复位端在制造测试、ATPG以及模拟初始化时可以对整个电路进行 快速复位。 5避免模块内的三态/双向 内部三态信号在制造测试和逻辑综合过程中难于处理. 近日读 J.Bhasker 的verilog synthesis practical primer , 受益匪浅,理清了不少基础电路知识 , 记下一些 tips :
1. 过程赋值(always 中触发赋值)的变量,可能会被综合成连线 或触发器 或锁存器.
2.综合成锁存器的规则:
a. 变量在条件语句(if 或case)中,被赋值.
b. 变量未在条件语句的所有分支中被赋值.
c. 在always语句多次调用之间需要保持变量值 .
以上三个条件必须同时满足.
3.综合成触发器的规则:
变量在时钟沿的控制下被赋值。
例外情况变量的赋值和引用都仅出现在一条always语句中则该变量被视为中
间变量而不是触发器。
4. 对于无时钟事情的always语句即组合逻辑建模其时间表应包括该alwa语
句引用的所有变量否则会出现RTL与Netlist的不一致
芯片外部引脚很多都使用inout类型的为的是节省管腿。一般信号线用做总线等双向数据传输的时候就要用到INOUT类型了。就是一个端口同时做输入和输出。 inout在具体实现上一般用三态门来实现。三态门的第三个状态就是高阻Z。 当inout端口不输出时将三态门置高阻。这样信号就不会因为两端同时输出而出错了,更详细的内容可以搜索一下三态门tri-state的资料. 1 使用inout类型数据,可以用如下写法: inout data_inout; input data_in; reg data_reg;//data_inout的映象寄存器reg link_data; assign data_inoutlink_data?data_reg:1’bz;//link_data控制三态门//对于data_reg,可以通过组合逻辑或者时序逻辑根据data_in对其赋值.通过控制link_data的高低电平,从而设置data_inout是输出数据还是处于高阻态,如果处于高阻态,则此时当作输入端口使用.link_data可以通过相关电路来控制. 2 编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而其它输入端口都定义成reg类型,这两者是有区别的.当上面例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可以断开.此时可以用assign语句实现:assign data_inoutlink?data_in_t:1’bz;其中的link ,data_in_t是reg类型变量,在测试模块中赋值.另外,可以设置一个输出端口观察data_inout用作输出的情况: Wire data_out; Assign data_out_t(!link)?data_inout:1’bz;
elsein RTL inout use in top module(PAD) dont use inout(tri) in sub module也就是说在内部模块最好不要出现inout如果确实需要那么用两个port实现到顶层的时候再用三态实现。理由是在非顶层模块用双向口的话该双向口必然有它的上层跟它相连。既然是双向口则上层至少有一个输入口和一个输出口联到该双向口上则发生两个内部输出单元连接到一起的情况出现这样在综合时往往会出错。
对双向口我们可以将其理解为2个分量一个输入分量一个输出分量。另外还需要一个控制信号控制输出分量何时输出。此时我们就可以很容易地对双向端口建模。
例子CODE: module dual_port ( .... inout_pin, .... );
inout inout_pin;
wire inout_pin;
wire input_of_inout; wire output_of_inout; wire out_en;
assign input_of_inout inout_pin;
assign inout_pin out_en ? output_of_inout : 高阻;
endmodule
可见此时input_of_inout和output_of_inout就可以当作普通信号使用了。
在仿真的时候需要注意双向口的处理。如果是直接与另外一个模块的双向口连接那么只要保证一个模块在输出的时候另外一个模块没有输出处于高阻态就可以了。如果是在ModelSim中作为单独的模块仿真那么在模块输出的时候不能使用force命令将其设为高阻态而是使用release命令将总线释放掉
很多初学者在写testbench进行仿真和验证的时候被inout双向口难住了。仿真器老是提示错误不能进行。下面是我个人对inout端口写testbench仿真的一些总结并举例进行说明。在这里先要说明一下inout口在testbench中要定义为wire型变量。
先假设有一源代码为
module xx(data_inout , ........);
inout data_inout;
........................
assign data_inout(! link)?datareg:1bz;
endmodule
方法一使用相反控制信号inout口等于两个模块之间用inout双向口互连。这种方法要注意assign 语句只能放在initial和always块内。
module test();
wire data_inout;
reg data_reg;
reg link;
initial begin
..........
end
assign data_inoutlink?data_reg:1bz;
endmodule
方法二使用force和release语句但这种方法不能准确反映双向端口的信号变化但这种方法可以反在块内。
module test();
wire data_inout;
reg data_reg;
reg link;
#xx; //延时
force data_inout1bx; //强制作为输入端口
...............
#xx;
release data_inout; //释放输入端口
endmodule
很多读者反映仿真双向端口的时候遇到困难这里介绍一下双向端口的仿真方法。一个典型的双向端口如图1所示。其中inner_port与芯片内部其他逻辑相连outer_port为芯片外部管脚out_en用于控制双向端口的方向out_en为1时端口为输出方向out_en为0时端口为输入方向。用Verilog语言描述如下module bidirection_io(inner_port,out_en,outer_port); input out_en; inout[7:0] inner_port; inout[7:0] outer_port; assign outer_port(out_en1)?inner_port:8hzz; assign inner_port(out_en0)?outer_port:8hzz; endmodule用VHDL语言描述双向端口如下library ieee; use IEEE.STD_LOGIC_1164.ALL; entity bidirection_io is port ( inner_port : inout std_logic_vector(7 downto 0); out_en : in std_logic; outer_port : inout std_logic_vector(7 downto 0) ); end bidirection_io; architecture behavioral of bidirection_io is begin outer_portinner_port when out_en1 else (OTHERSZ); inner_portouter_port when out_en0 else (OTHERSZ); end behavioral;仿真时需要验证双向端口能正确输出数据以及正确读入数据因此需要驱动out_en端口当out_en端口为1时testbench驱动inner_port端口然后检查outer_port端口输出的数据是否正确当out_en端口为0时testbench驱动outer_port端口然后检查inner_port端口读入的数据是否正确。由于inner_port和outer_port端口都是双向端口在VHDL和Verilog语言中都用inout定义因此驱动方法与单向端口有所不同。验证该双向端口的testbench结构如图2所示。这是一个self-checking testbench可以自动检查仿真结果是否正确并在Modelsim控制台上打印出提示信息。图中Monitor完成信号采样、结果自动比较的功能。testbench的工作过程为1out_en1时双向端口处于输出状态testbench给inner_port_tb_reg信号赋值然后读取outer_port_tb_wire的值如果两者一致双向端口工作正常。2out_en0时双向端口处于输如状态testbench给outer_port_tb_reg信号赋值然后读取inner_port_tb_wire的值如果两者一致双向端口工作正常。用Verilog代码编写的testbench如下其中使用了自动结果比较随机化激励产生等技术。timescale 1ns/10ps module tb(); reg[7:0] inner_port_tb_reg; wire[7:0] inner_port_tb_wire; reg[7:0] outer_port_tb_reg; wire[7:0] outer_port_tb_wire; reg out_en_tb; integer i; initial begin out_en_tb0; inner_port_tb_reg0; outer_port_tb_reg0; i0; repeat(20) begin #50 i$random; out_en_tbi[0]; //randomize out_en_tb inner_port_tb_reg$random; //randomize data outer_port_tb_reg$random; end end //**** drive the ports connecting to bidirction_io assign inner_port_tb_wire(out_en_tb1)?inner_port_tb_reg:8hzz; assign outer_port_tb_wire(out_en_tb0)?outer_port_tb_reg:8hzz; //instatiate the bidirction_io module bidirection_io bidirection_io_inst(.inner_port(inner_port_tb_wire), .out_en(out_en_tb), .outer_port(outer_port_tb_wire)); //***** monitor ****** always(out_en_tb,inner_port_tb_wire,outer_port_tb_wire) begin #1; if(outer_port_tb_wireinner_port_tb_wire) begin $display(\n **** time%t ****,$time); $display(OK! out_en%d,out_en_tb); $display(OK! outer_port_tb_wire%d,inner_port_tb_wire%d, outer_port_tb_wire,inner_port_tb_wire); end else begin $display(\n **** time%t ****,$time); $display(ERROR! out_en%d,out_en_tb); $display(ERROR! outer_port_tb_wire ! inner_port_tb_wire ); $display(ERROR! outer_port_tb_wire%d, inner_port_tb_wire%d, outer_port_tb_wire,inner_port_tb_wire); end end endmodule