刘航宇 发布的文章 - 我的学记|刘航宇的博客
首页
📊归档
⏳时光机
📬留言
🐾友链
资助名单
推荐
🎓843课程班
🖼️相册
🎵音乐
🏞️壁纸
搜 索
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
11,221 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
6,353 阅读
3
【高数】形心计算公式讲解大全
5,735 阅读
4
【1】基于STM32CubeMX-STM32GPIO端口开发
4,477 阅读
5
如何判断运放是工作在线性区还是非线性区
4,201 阅读
🌻微语&随笔
励志美文
我的随笔
写作办公
📖电子&通信
电路&嵌入式
通信&信息处理
编程&脚本笔记
🗜️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
搜 索
标签搜索
嵌入式
ASIC/FPGA
VLSI
SOC设计
机器学习
天线设计
C/C++
EDA&虚拟机
小实验
软件算法
信号处理
电子线路
通信&射频
随笔
笔试面试
硬件算法
Verilog
软件无线电
Python
DL/ML
刘航宇Hangyu Liu
嵌入式系统&数字IC爱好者博客
累计撰写
296
篇文章
累计收到
522
条评论
首页
栏目
🌻微语&随笔
励志美文
我的随笔
写作办公
📖电子&通信
电路&嵌入式
通信&信息处理
编程&脚本笔记
🗜️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
页面
📊归档
⏳时光机
📬留言
🐾友链
资助名单
推荐
🎓843课程班
🖼️相册
🎵音乐
🏞️壁纸
用户登录
登录
刘航宇(共296篇)
找到
296
篇与
刘航宇
相关的结果
【硬件算法进阶】Verilog实现802.3 CRC-32校验运算电路
循环冗余校验(Cyclic Redundancy Check,CRC)是通信中常用的差错检测编码方式,其基本工作原理是根据输入的信息位(信息码元),按照给定的生成多项式产生校验位(校验码元),并一起传送到接收端。在接收端,接收电路按照相同的规则对接收数据进行计算并生成本地的校验位,然后与收到的校验位进行对比,如果二者不同,则说明传输过程中发生了错误,否则说明传输是正确的。带有CRC校验结果的数据帧结构如表1-2所示。CRC检验位生成与检测工作包括以下基本步骤。图1-6是一个并行CRC-32校验运算电路。图中的d[7:0]是输入的用户数据,它是按照字节的方式输入的。load_ini是在对一个新的数据包开始校验计算之前对电路进行初始化的控制信号,经过初始化后,电路内部32比特寄存器的值改变为全1。calc是电路运算指示信号,在整个数据帧输入和CRC校验结果输出的过程中其都应该保持有效(高电平有效)。d_valid为1时表示当前输入的是需要进行校验运算的有效数据。crc[7:0]是电路输出的CRC校验运算结果,它是按照字节方式,在有效数据输入完成后开始输出的,一共有4个有效字节。crc_reg[31:0]是内部寄存器的值,具体使用时不需要该输出。并行计算的思想,输入数据S要并行输入到G(x)系数为1的支路中,输入数据从输入端按高到低逐bit输入,就可以实现。假如被除数是2位的数据S[1:0]=01,多项式是10011,x4 +x+1。在CRC校验里面,习惯省略最高位的1,多项式用0011表示。那么S除以0011的模二运算数字电路结构为:其中d1~ d4是寄存器输入;q1~q4是寄存器输出。寄存器需要赋初值,一般赋全1或全0。d1=S[1]^q4;d2= S[1]^ q1^q4;d3=q2;d4=q3。经过一次移位后:q1=d1= S[1]^q4;q2= d2= S[1]^ q1^q4;q3= d3=q2;q4= d4=q3。此时有:d1=S[0]^q3;d2= S[0]^ S[1]^ q4^q3;d3= S[1]^ q1^q4;d4= q2。令c[3:0]=,d[3:0]=,那么d就是最终的运算结果表达式,如下d[3]=c[1];d[2]= S[1]^ c[0]^c[3];d[1]= S[0]^ S[1]^ c[3]^ c[2];d[0]= S[0]^ c[2]。令c的初值为0,则01对0011的模二除法的余数为0011。再比如多项式为x5 +x3 +x+1,简记式为01011,其数字电路结构为:输入数据S要全部输入完,寄存器得到的结果才是最后的结果。同理可推导出其他多项式和输入数据的情况。对于循环检验,这里举个例子,如果数据是10bit*100个包,则每次输入10bit得到校验码后,该检验码为下次数据计算时寄存器D的初值,如此反复计算得到最后的检验码添加到整个数据后面即可,而不需要每个数据包后面都添加检验码。下面是以太网循环冗余校验电路的设计代码:module crc32_8023( clk, reset, d, load_init, calc, d_valid, crc_reg, crc ); input clk; input reset; input [7:0] d; input load_init; input calc; input d_valid; output reg [31:0] crc_reg; output reg [7:0] crc; wire [2:0] ctl; wire [31:0] next_crc; wire [31:0] i; assign i = crc_reg; assign ctl = ; always @(posedge clk or posedge reset) begin if(reset) crc_reg <= 32'hffffffff; else begin case (ctl) // 3'b000,3'b010: begin crc_reg <= crc_reg; crc <= crc;end 3'b001: begin crc_reg <= ; crc <= ~; //crc <= ~ crc_reg[16:23]; end 3'b011: begin crc_reg <= next_crc[31:0]; crc <= ~; //crc <= ~ next_crc[24:31]; end 3'b100,3'b110: begin crc_reg <= 32'hffffffff; crc <= crc; end 3'b101: begin crc_reg <= 32'hffffffff; crc <= ~; //crc <= ~ crc_reg[16:23]; end 3'b111: begin crc_reg <= 32'hffffffff; crc <= ~; //crc <= ~ next_crc[24:31]; end endcase end end assign next_crc[0] = d[7]^i[24]^d[1]^i[30]; //d+i=31 assign next_crc[1] = d[6]^d[0]^d[7]^d[1]^i[24]^i[25]^i[30]^i[31]; assign next_crc[2] = d[5]^d[6]^d[0]^d[7]^d[1]^i[24]^i[25]^i[26]^i[30]^i[31]; assign next_crc[3] = d[4]^d[5]^d[6]^d[0]^i[25]^i[26]^i[27]^i[31]; assign next_crc[4] = d[3]^d[4]^d[5]^d[7]^d[1]^i[24]^i[26]^i[27]^i[28]^i[30]; assign next_crc[5] = d[0]^d[1]^d[2]^d[3]^d[4]^d[6]^d[7]^i[24]^i[25]^i[27]^i[28]^i[29]^i[30]^i[31]; assign next_crc[6] = d[0]^d[1]^d[2]^d[3]^d[5]^d[6]^i[25]^i[26]^i[28]^i[29]^i[30]^i[31]; assign next_crc[7] = d[0]^d[2]^d[4]^d[5]^d[7]^i[24]^i[26]^i[27]^i[29]^i[31]; assign next_crc[8] = d[3]^d[4]^d[6]^d[7]^i[24]^i[25]^i[27]^i[28]^i[0]; //每项多出i[i],i=0、1、2...23 assign next_crc[9] = d[2]^d[3]^d[5]^d[6]^i[1]^i[25]^i[26]^i[28]^i[29]; assign next_crc[10] =d[2]^d[4]^d[5]^d[7]^i[2]^i[24]^i[26]^ i[27]^i[29]; assign next_crc[11] =i[3]^d[3]^i[28]^d[4]^i[27]^d[6]^i[25]^d[7]^i[24]; assign next_crc[12] =d[1]^d[2]^d[3]^d[5]^d[6]^d[7]^i[4]^i[24]^i[25]^i[26]^i[28]^i[29]^i[30]; assign next_crc[13] =d[0]^d[1]^d[2]^d[4]^d[5]^d[6]^i[5]^i[25]^i[26]^i[27]^i[29]^i[30]^i[31]; assign next_crc[14] =d[0]^d[1]^d[3]^d[4]^d[5]^i[6]^i[26]^i[27]^i[28]^i[30]^i[31]; assign next_crc[15] =d[0]^d[2]^d[3]^d[4]^i[7]^i[27]^i[28]^i[29]^i[31]; assign next_crc[16] =d[2]^d[3]^d[7]^i[8]^i[24]^i[28]^i[29]; assign next_crc[17] =d[1]^d[2]^d[6]^i[9]^i[25]^i[29]^i[30]; assign next_crc[18] =d[0]^d[1]^d[5]^i[10]^i[26]^i[30]^i[31]; assign next_crc[19] =d[0]^d[4]^i[11]^i[27]^i[31]; assign next_crc[20] =d[3]^i[12]^i[28]; assign next_crc[21] =d[2]^i[13]^i[29]; assign next_crc[22] =d[7]^i[14]^i[24]; assign next_crc[23] =d[1]^d[6]^d[7]^i[15]^i[24]^i[25]^i[30]; assign next_crc[24] =d[0]^d[5]^d[6]^i[16]^i[25]^i[26]^i[31]; assign next_crc[25] =d[4]^d[5]^i[17]^i[26]^i[27]; assign next_crc[26] =d[1]^d[3]^d[4]^d[7]^i[18]^i[28]^i[27]^i[24]^i[30]; assign next_crc[27] =d[0]^d[2]^d[3]^d[6]^i[19]^i[29]^i[28]^i[25]^i[31]; assign next_crc[28] =d[1]^d[2]^d[5]^i[20]^i[30]^i[29]^i[26]; assign next_crc[29] =d[0]^d[1]^d[4]^i[21]^i[31]^i[30]^i[27]; assign next_crc[30] =d[0]^d[3]^i[22]^i[31]^i[28]; assign next_crc[31] =d[2]^i[23]^i[29]; endmodule测试代码`timescale 1ns/1ns module crc_test(); reg clk, reset; reg [7:0] d; reg load_init; reg calc; reg data_valid; wire [31:0] crc_reg; wire [7:0] crc; initial begin clk=0; reset=0; load_init=0; calc=0; data_valid=0; d=0; end always begin #10 clk=1; #10 clk=0; end always begin crc_reset; crc_cal; end task crc_reset; begin reset=1; repeat(2)@(posedge clk); #5; reset=0; repeat(2)@(posedge clk); end endtask task crc_cal; begin repeat(5) @ (posedge clk); //通过losd_init=1 对CRC计算电路进行初始化 #5; load_init= 1; repeat(1)@ (posedge clk); //设置1oad_init=0,data_valid= 1,calc=1 //开始对输人数据进行CRC校验运算 #5; load_init= 0; data_valid=1; calc=1; d=8'haa; repeat(1)@ (posedge clk); #5; data_valid=1; calc=1; d=8'hbb; repeat(1)@ (posedge clk); #5; data_valid=1; calc=1; d=8'hcc; repeat(1)@ (posedge clk); #5; data_valid=1; calc=1; d=8'hdd; repeat(1)@ (posedge clk); //设置load_init=0,data_valid=1,calc=0 //停止对数据进行CRC校验运算,开始输出 //计算结果 #5; data_valid=1; calc=0; d=8'haa; repeat(1)@ (posedge clk); #5; data_valid=1; calc=0; d=8'hbb; repeat(1)@ (posedge clk); #5; data_valid=1; calc=0; d=8'hee; repeat(1)@ (posedge clk); #5; data_valid=1; calc=0; d=8'hdd; repeat(1)@ (posedge clk); #5; data_valid=0; repeat(10)@ (posedge clk); end endtask crc32_8023 my_crc_test(.clk(clk),.reset(reset),.d(d),.load_init(load_init),.calc(calc),.d_valid(data_valid),.crc_reg(crc_reg),.crc(crc)); endmodule图1-7是电路的仿真结果。图中①是电路进行CRC校验计算之前对电路进行初始化操作的过程,经过初始化之后,crc_reg内部数值为全1。②是对输入数据aa-> bb-> cc-> dd进行运算操作的过程,此时calc和data_valid均为1。③是输出计算结果的过程,CRC校验运算结果a7、01、b4和55先后被输出。在接收方向上,可以采用相同的电路进行校验检查,判断是否在传输过程中发生了差错。具体工作时,可以边接收用户数据边进行校验运算,当一个完整的MAC帧接收完成后(此时接收数据帧中的校验结果也参加了校验运算),如果当前校验电路的crc_reg值为0xC704DD7B(对于以太网中使用的CRC-32校验,无论原始数据是什么,正确接收时校验和都是此固定数值),说明没有发生错误,否则说明MAC帧有错。CRC-32校验值的作用是用于检测数据传输或存储中的错误。发送数据时,会根据数据内容生成简短的校验和,并将其与数据一起发送。接收数据时,将再次生成校验和并将其与发送的校验和进行比较。如果两者相等,则没有数据损坏。如果两者不相等,则说明数据在传输或存储过程中发生了改变,可能是由于噪声、干扰、故障或恶意篡改等原因造成的。CRC-32校验值可以有效地检测出数据中的随机错误,但是不能保证检测出所有的错误。例如,如果数据中有偶数个比特发生了翻转,那么CRC-32校验值可能不会改变,从而无法发现错误。因此,CRC-32校验值只能作为一种辅助的错误检测手段,不能完全依赖它来保证数据的正确性和完整性。相关工具如果不理解推导过程的话,可以由相关工具帮忙计算出结果和得到Verilog代码:CRC校验Verilog代码生成链接:http://outputlogic.com/?page_id=321CRC校验计算工具链接:http://www.ip33.com/crc.html,这个工具只能计算16bit为一个数据包的数据,如果数据包为10bit等之类的就不太适用在线计算器使用举例报文 : 1011001 (0x59)生成多项式 : g(x) = x^4 + x^3 + 1CRC : 1010 ( 0xa)CRC计算结果截图:参考文献Verilog HDL算法与电路设计-乔庐峰
2023年04月12日
944 阅读
0 评论
2 点赞
2023-04-11
Design Compile(DC)优化性、高性能性综合
compile_ultra具体使用方法参考DC manualcompile_ultra跟compile一样,是进行编译的命令。compile_ultra命令适用于时序要求比较严格,高性能的设计。使用该命令可以得到更好的延迟质量( delay QoR ),特别适用于高性能的算术电路优化。该命令非常容易使用,它自动设置所有所需的选项和变量。下面是这个命令的一些介绍:compile_ultra命令包含了以时间为中心的优化算法,在编辑过程中使用的算法有:compile_ultra命令支持DFT流程,此外compile_ultra命令非常简单易用,它的开关选项有:上面的开关部分说明如下所示:使用compile_ultra命令时,如使用下面变量的设置,所有的DesignWare层次自动地被取消: set compile_ultra_ungroup_dw true (默认值为true) 也就是说,你调用的一个加法器和一个乘法器,本来他们是以IP核的形式,或者说是以模块的形式进行综合的,但是设置了上面那么变量之后,综合后那个模块的界面就没有了,你不知道哪些门电路是加法器的,哪些是乘法器的。 使用compile_ultra命令时,使用下面的变量设置,如果设计中有一些模块的规模小于或等于变量的值,模块层次被自动取消: set compile_auto_ungroup_delay_num_cells 100(默认值=500) 也就是说,假设你有一个模块A是一个小的乘法器,并且调用了模块B,一个模块B是一个小的加法器,使用没有设置这条命令的情况综合,那么我们可以看到模块A中乘法器对应的门电路是哪些,同样也可以看到模块B的加法器是由哪些门电路构成的,模块A和模块B之间有层次、有界限;当设置上面的那条命令之后,我们就看不到模块A或者模块B之间的层次关系了,也看不到乘法器是由哪些门电路构成,或者说你看到了某一个与门,但是你并不知道它是构成乘法器的还是构成加法器的。
2023年04月11日
269 阅读
0 评论
2 点赞
【IC/CPU设计】极简的RISC_CPU设计
CPU为SOC系统的核心环节,该项目来自于夏宇闻老师的经典教材——《Verilog 数字系统设计教程》,通过此练习方便数字ICer更好的入门本次项目实践环境:前仿: Modelsim综合: Design CompileCPU简介CPU(Central Processing Unit),中文全称中央处理器,作为四大U之首(CPU/GPU/TPU/NPU),是计算机系统的运算和控制核心,也是当今数字系统中不可或缺的组成部分。CPU自诞生到如今发展超过50年,借助冯诺依曼体系,CPU掀起一股又一股的科技浪潮。RISC作为精简了指令集的CPU,除了指令更加简洁,还拥有简单合理的内部结构,从而提高了运算速度。CPU工作的5个阶段:(1)取指(IF,Instruction Fetch),将指令从存储器取出到指令寄存器。每取一条指令,程序计数器自加一。(2)译指(ID,Instruction Decode),对取出的指令按照规定格式进行拆分和译码。(3)执行(EX,Execute),执行具体指令操作。(4)访问存储(MEM,Memory),根据指令访问存储、完成存储和读取。(5)写回(WB,Write Back),将计算结果写回到存储器。CPU内部关键结构:(1)算术逻辑运算器(ALU);(2)累加器;(3)程序计数器;(4)指令寄存器和译码器;(5)时序和控制部件。RISC_CPU内部结构和Verilog实现本项目中的RISC_CPU一共有9个模块组成,具体如下:(1)时钟发生器;(2)指令寄存器;(3)累加器;(4)算术逻辑运算单元;(5)数据控制器;(6)状态控制器;(7)主状态机;(8)程序计数器;(9)地址多路器。整体结构时钟发生器模块图:端口描述:reset是高电平复位信号;clk是外部时钟信号;fetch是控制信号,是clk的八分频信号;fetch为高电平时,触发执行指令以及地址多路器输出指令地址和数据地址。alu_ena是算术逻辑运算单元的使能信号。可以看到alu_ena提前fetch高电平一个clk周期,fetch是clk的8分频信号。Verilog代码:// Description: RISC——CPU 时钟发生器 // ----------------------------------------------------------------------------- module clk_gen ( input clk , // Clock input reset , // High level reset output reg fetch , // 8 frequency division output reg alu_ena // Arithmetic enable ); reg [7:0] state; //One-piece state machine parameter S1 = 8'b0000_0001, S2 = 8'b0000_0010, S3 = 8'b0000_0100, S4 = 8'b0000_1000, S5 = 8'b0001_0000, S6 = 8'b0010_0000, S7 = 8'b0100_0000, S8 = 8'b1000_0000, idle = 8'b0000_0000; always@(posedge clk)begin if(reset)begin fetch <= 0; alu_ena <= 0; state <= idle; end else begin case(state) S1: begin alu_ena <= 1; state <= S2; end S2: begin alu_ena <= 0; state <= S3; end S3: begin fetch <= 1; state <=S4; end S4: begin state <= S5; end S5: state <= S6; S6: state <= S7; S7: begin fetch <= 0; state <= S8; end S8: begin state <= S1; end idle: state <= S1; default: state <=idle; endcase end end endmodule指令寄存器模块图:端口描述:寄存器是将数据总线送来的指令存入高8位或低8位寄存器中。ena信号用来控制是否寄存。每条指令为两个字节,16位,高3位是操作码,低13位是地址(CPU地址总线为13位,寻址空间为8K字节)。本设计的数据总线为8位,每条指令需要取两次,先取高8位,再取低8位。Verilog代码:// Description: RISC—CPU 指令寄存器 // ----------------------------------------------------------------------------- module register ( input [7:0] data , input clk , input rst , input ena , output reg [15:0] opc_iraddr ); reg state ; // always@( posedge clk ) begin if( rst ) begin opc_iraddr <= 16'b 0000_0000_0000_0000; state <= 1'b 0; end // if rst // If load_ir from machine actived, load instruction data from rom in 2 clock periods. // Load high 8 bits first, and then low 8 bits. else if( ena ) begin case( state ) 1'b0 : begin opc_iraddr [ 15 : 8 ] <= data; state <= 1; end 1'b1 : begin opc_iraddr [ 7 : 0 ] <= data; state <= 0; end default : begin opc_iraddr [ 15 : 0 ] <= 16'bxxxx_xxxx_xxxx_xxxx; state <= 1'bx; end endcase // state end // else if ena else state <= 1'b0; end endmodule 累加器模块图:端口描述:累加器用于存放当前结果,ena信号有效时,在clk上升沿输出数据总线的数据。// Description: RISC-CPU 累加器模块 // ----------------------------------------------------------------------------- module accum ( input clk , // Clock input ena , // Enable input rst , // Asynchronous reset active high input [7:0] data , // Data bus output reg [7:0] accum ); always@(posedge clk)begin if(rst) accum <= 8'b0000_0000;//Reset else if(ena) accum <= data; end endmodule 算术运算器模块图:端口描述:算术逻辑运算单元可以根据输入的操作码分别实现相应的加、与、异或、跳转等基本操作运算。本单元支持8种操作运算。opcode用来选择计算模式data是数据输入accum是累加器输出alu_ena是模块使能信号clk是系统时钟Verilog代码:// Description: RISC-CPU 算术运算器 // ----------------------------------------------------------------------------- module alu ( input clk , // Clock input alu_ena , // Enable input [2:0] opcode , // High three bits are used as opcodes input [7:0] data , // data input [7:0] accum , // accum out output reg [7:0] alu_out , output zero ); parameter HLT = 3'b000 , SKZ = 3'b001 , ADD = 3'b010 , ANDD = 3'b011 , XORR = 3'b100 , LDA = 3'b101 , STO = 3'b110 , JMP = 3'b111 ; always @(posedge clk) begin if(alu_ena) begin casex(opcode)//操作码来自指令寄存器的输出 opc_iaddr(15..0)的第三位 HLT: alu_out <= accum ; SKZ: alu_out <= accum ; ADD: alu_out <= data + accum ; ANDD: alu_out <= data & accum ; XORR: alu_out <= data ^ accum ; LDA : alu_out <= data ; STO : alu_out <= accum ; JMP : alu_out <= accum ; default: alu_out <= 8'bxxxx_xxxx ; endcase end end assign zero = !accum; endmodule 数据控制器模块图:端口描述:数据控制器的作用是控制累加器的数据输出,数据总线是分时复用的,会根据当前状态传输指令或者数据。数据只在往RAM区或者端口写时才允许输出,否则呈现高阻态。in是8bit数据输入data_ena是使能信号data是8bit数据输出Verilog代码:// Description: RISC-CPU 数据控制器 // ----------------------------------------------------------------------------- module datactl ( input [7:0] in , // Data input input data_ena , // Data Enable output wire [7:0] data // Data output ); assign data = (data_ena )? in: 8'bzzzz_zzzz ; endmodule 地址多路器模块图:端口描述:用于选择输出地址是PC(程序计数)地址还是数据/端口地址。每个指令周期的前4个时钟周期用于从ROM种读取指令,输出的是PC地址;后四个时钟周期用于对RAM或端口读写。地址多路器和数据控制器实现的功能十分相似。fetch信号用来控制地址输出,高电平输出pc_addr ,低电平输出ir_addr ;pc_addr 指令地址;ir_addr ram或端口地址。Verilog代码:// Description: RISC-CPU 地址多路器 // ----------------------------------------------------------------------------- module adr ( input fetch , // enable input [12:0] ir_addr , // input [12:0] pc_addr , // output wire [12:0] addr ); assign addr = fetch? pc_addr :ir_addr ; endmodule 程序计数器模块图:端口描述:程序计数器用来提供指令地址,指令按照地址顺序存放在存储器中。包含两种生成途径:(1)顺序执行的情况(2)需要改变顺序,例如JMP指令rst复位信号,高电平时地址清零;clock 时钟信号,系统时钟;ir_addr目标地址,当加载信号有效时输出此地址;pc_addr程序计数器地址load地址装载信号Verilog代码:// Description: RISC-CPU 程序计数器 // ----------------------------------------------------------------------------- module counter ( input [12:0] ir_addr , // program address input load , // Load up signal input clock , // CLock input rst , // Reset output reg [12:0] pc_addr // insert program address ); always@(posedge clock or posedge rst) begin if(rst) pc_addr <= 13'b0_0000_0000_0000; else if(load) pc_addr <= ir_addr; else pc_addr <= pc_addr + 1; end endmodule 状态控制器&主状态机模块图:(图左边)状态机端口描述:状态控制器接收复位信号rst,rst有效,控制输出ena为0,fetch有效控制ena为1。// Description: RISC-CPU 状态控制器 // ----------------------------------------------------------------------------- module machinectl ( input clk , // Clock input rst , // Asynchronous reset input fetch , // Asynchronous reset active low output reg ena // Enable ); always@(posedge clk)begin if(rst) ena <= 0; else if(fetch) ena <=1; end endmodule (图右边)主状态端口描述:主状态机是CPU的控制核心,用于产生一系列控制信号。指令周期由8个时钟周期组成,每个时钟周期都要完成固定的操作。(1)第0个时钟,CPU状态控制器的输出rd和load_ir 为高电平,其余为低电平。指令寄存器寄存由ROM送来的高8位指令代码。(2)第1个时钟,与上一个时钟相比只是inc_pc从0变为1,故PC增1,ROM送来低8位指令代码,指令寄存器寄存该8位指令代码。(3)第2个时钟,空操作。(4)第3个时钟,PC增1,指向下一条指令。操作符为HLT,输出信号HLT为高。操作符不为HLT,除PC增1外,其余控制线输出为0.(5)第4个时钟,操作。操作符为AND,ADD,XOR或LDA,读取相应地址的数据;操作符为JMP,将目的地址送给程序计数器;操作符为STO,输出累加器数据。(6)第5个时钟,若操作符为ANDD,ADD或者XORR,算术运算器完成相应的计算;操作符为LDA,就把数据通过算术运算器送给累加器;操作符为SKZ,先判断累加器的值是否为0,若为0,PC加1,否则保持原值;操作符为JMP,锁存目的地址;操作符为STO,将数据写入地址处。(7)第6个时钟,空操作。(8)第7个时钟,若操作符为SKZ且累加器为0,则PC值再加1,跳过一条指令,否则PC无变化。// Description: RISC-CPU 主状态机 // ----------------------------------------------------------------------------- module machine ( input clk , // Clock input ena , // Clock Enable input zero , // Asynchronous reset active low input [2:0] opcode , // OP code output reg inc_pc , // output reg load_acc , // output reg load_pc , // output reg rd , // output reg wr , // output reg load_ir , // output reg datactl_ena , // output reg halt ); reg [2:0] state ; //parameter parameter HLT = 3'b000 , SKZ = 3'b001 , ADD = 3'b010 , ANDD = 3'b011 , XORR = 3'b100 , LDA = 3'b101 , STO = 3'b110 , JMP = 3'b111 ; always@(negedge clk) begin if(!ena) //收到复位信号rst,进行复位操作 begin state <= 3'b000; <= 4'b0000; <= 4'b0000; end else ctl_cycle; end //------- task ctl_cycle ------- task ctl_cycle; begin casex(state) 3'b000: //load high 8bits in struction begin <= 4'b0001; <= 4'b0100; state <= 3'b001; end 3'b001://pc increased by one then load low 8bits instruction begin <= 4'b1001; <= 4'b0100; state <= 3'b010; end 3'b010: //idle begin <= 4'b0000; <= 4'b0000; state <= 3'b011; end 3'b011: //next instruction address setup 分析指令开始点 begin if(opcode == HLT)//指令为暂停HLT begin <= 4'b1000; <= 4'b0001; end else begin <= 4'b1000; <= 4'b0000; end state <= 3'b100; end 3'b100: //fetch oprand begin if(opcode == JMP) begin <= 4'b0010; <= 4'b0000; end else if(opcode == ADD || opcode == ANDD || opcode == XORR || opcode == LDA) begin <= 4'b0001; <= 4'b0000; end else if(opcode == STO) begin <= 4'b0000; <= 4'b0010; end else begin <= 4'b0000; <= 4'b0000; end state <= 3'b101; end 3'b101://operation begin if(opcode == ADD || opcode == ANDD ||opcode ==XORR ||opcode == LDA)//过一个时钟后与累加器的内存进行运算 begin <= 4'b0101; <= 4'b0000; end else if(opcode == SKZ && zero == 1)// & and && begin <= 4'b1000; <= 4'b0000; end else if(opcode == JMP) begin <= 4'b1010; <= 4'b0000; end else if(opcode == STO) begin//过一个时钟后吧wr变为1,写到RAM中 <= 4'b0000; <= 4'b1010; end else begin <= 4'b0000; <= 4'b0000; end state <= 3'b110; end 3'b110: begin if(opcode == STO) begin <= 4'b0000; <= 4'b0010; end else if(opcode == ADD || opcode == ANDD || opcode == XORR || opcode == LDA) begin <= 4'b0001; <= 4'b0000; end else begin <= 4'b0000; <= 4'b0000; end state <= 3'b111; end 3'b111: begin if(opcode == SKZ && zero == 1) begin <= 4'b1000; <= 4'b0000; end else begin <= 4'b0000; <= 4'b0000; end state <= 3'b000; end default: begin <= 4'b0000; <= 4'b0000; state <= 3'b000; end endcase end endtask endmodule 外围模块为了对RISC-CPU进行测试,需要对ROM、RAM和地址译码器进行设计。地址译码器模块说明:地址译码器用于产生选通信号,选通ROM或者RAM1FFFH —— 1800H RAM(范围):1_1xxx_xxxx_xxxx17FFH —— 0000H ROM(范围):0_xxxx_xxxx_xxxx+1_0xxx_xxxx_xxxxVerilog代码:// Description: RISC-CPU 地址译码器 // ----------------------------------------------------------------------------- module addr_decode ( input [12:0] addr , // Address output reg ram_sel , // Ram sel output reg rom_sel // Rom sel ); always@(addr)begin casex(addr) 13'b1_1xxx_xxxx_xxxx: <= 2'b01; 13'b0_xxxx_xxxx_xxxx: <= 2'b10; 13'b1_0xxx_xxxx_xxxx: <= 2'b10; default: <= 2'b00; endcase end endmodule RAM模块说明:RAM用于存放临时数据,可读可写。Verilog代码:// Description: RISC-CPU RAM模块 // ----------------------------------------------------------------------------- module ram ( input ena , // Enable input read , // read Enable input write , // write Enable inout wire [7:0] data , // data input [9:0] addr // address ); reg [7:0] ram [10'h3ff:0] ; assign data = (read && ena )? ram[addr]:8'h zz; always@(posedge write) begin ram[addr] <= data; end endmodule ROM模块说明:RAM用于存放只读数据。Verilog代码:// Description: RISC-CPU ROM模块 // ----------------------------------------------------------------------------- module rom ( input [12:0] addr , input read , input ena , output wire [7:0] data ); reg [7:0] memory [13'h1ff:0]; assign data = (read && ena)? memory[addr]:8'b zzzz_zzzz; endmodule 顶层模块模块图:Verilog代码:// Description: RISC-CPU 顶层模块 // ----------------------------------------------------------------------------- //`include "clk_gen.v" //`include "accum.v" //`include "adr.v" //`include "alu.v" //`include "machine.v" //`include "counter.v" //`include "machinectl.v" //`iclude "machine.v" //`include "register.v" //`include "datactl.v" module RISC_CPU ( input clk , input reset , output wire rd , output wire wr , output wire halt , output wire fetch , //addr output wire [12:0] addr , output wire [12:0] ir_addr , output wire [12:0] pc_addr , inout wire [7:0] data , //op output wire [2:0] opcode ); wire [7:0] alu_out ; wire [7:0] accum ; wire zero ; wire inc_pc ; wire load_acc ; wire load_pc ; wire load_ir ; wire data_ena ; wire contr_ena ; wire alu_ena ; //inst clk_gen mclk_gen( .clk (clk ), .reset (reset ), .fetch (fetch ), .alu_ena (alu_ena ) ); register m_register( .data (data ), .ena (load_ir ), .rst (reset ), .clk (clk ), .opc_iraddr ( ) ); accum m_accum( .data (alu_out ), .ena (load_acc ), .clk (clk ), .rst (reset ), .accum (accum ) ); alu m_alu( .data (data ), .accum (accum ), .clk (clk ), .alu_ena (alu_ena ), .opcode (opcode ), .alu_out (alu_out ), .zero (zero ) ); machinectl m_machinectl( .clk (clk ), .rst (reset ), .fetch (fetch ), .ena (contr_ena ) ); machine m_machine( .inc_pc (inc_pc ), .load_acc (load_acc ), .load_pc (load_pc ), .rd (rd ), .wr (wr ), .load_ir (load_ir ), .clk (clk ), .datactl_ena(data_ena ), .halt (halt ), .zero (zero ), .ena (contr_ena ), .opcode (opcode ) ); datactl m_datactl( .in (alu_out ), .data_ena (data_ena ), .data (data ) ); adr m_adr( .fetch (fetch ), .ir_addr (ir_addr ), .pc_addr (pc_addr ), .addr (addr ) ); counter m_counter( .clock (inc_pc ), .rst (reset ), .ir_addr (ir_addr ), .load (load_pc ), .pc_addr (pc_addr ) ); endmodule TestbenchTestbench包含三个测试程序,这个部分不能综合。Test1程序TEST1程序用于验证RISC-CPU的逻辑功能,根据汇编语言由人工编译的。若各条指令正确,应该在地址2E(hex)处,在执行HLT时刻停止。若程序在任何其他位置停止,则必有一条指令运行错误,可以按照注释找到错误的指令。test1汇编程序:(.pro文件/存放于ROM)//机器码-地址-汇编助记符-注释 @00 //address statement 111_0000 //00 BEGIN: JMP TST_JMP 0011_1100 000_0000 //02 HLT //JMP did not work 0000_0000 000_00000 //04 HLT //JMP did not load PC skiped 0000_0000 101_1100 //06 JMP_OK: LDA DATA 0000_0000 001_00000 //08 SKZ 0000_0000 000_0000 //0a HLT 0000_0000 101_11000 //0C LDA DATA_2 0000_0001 001_00000 //0E SKZ 0000_0000 111_0000 //10 JMP SKZ_OK 001_0100 000_0000 //12 HLT 0000_0000 110_11000 //14 SKZ_OK: STO TEMP 0000_0010 101_11000 //16 LDA DATA_1 0000_0000 110_11000 //18 STO TEMP 0000_0010 101_11000 //1A LDA TEMP 0000_0010 001_00000 //1C SKZ 0000_0000 000_00000 //1E HLT 0000_0000 100_11000 //20 XOR DATA_2 0000_0001 001_00000 //22 SKZ 0000_0000 111_00000 //24 JMP XOR_OK 0010_1000 000_00000 //26 HLT 0000_0000 100_11000 //28 XOR_OK XOR DATA_2 0000_0001 001_00000 //2A SKZ 0000_0000 000_00000 //2C HLT 0000_0000 000_0000 //2E END 0000_0000 111_00000 //30 JMP BEGIN 0000_0000 @3c 111_00000 //3c TST_JMP IMR OK 0000_0110 000_00000 //3E HLT test1数据文件:(.dat/存放于RAM)/----------------------------------- @00 ///address statement at RAM 00000000 //1800 DATA_1 11111111 //1801 DATA_2 10101010 //1082 TEMPTest2程序TEST1程序用于验证RISC-CPU的逻辑功能,根据汇编语言由人工编译的。这个程序是用来测试RISC-CPU的高级指令集,若执行正确,应在地址20(hex)处在执行HLT时停止。test2汇编程序:@00 101_11000 //00 BEGIN 0000_0001 011_11000 //02 AND DATA_3 0000_0010 100_11000 //04 XOR DATA_2 0000_0001 001_00000 //06 SKZ 0000_0000 000_00000 //08 HLT 0000_0000 010_11000 //0A ADD DATA_1 0000_0000 001_00000 //0C SKZ 0000_0000 111_00000 //0E JMP ADD_OK 0001_0010 111_00000 //10 HLT 0000_0000 100_11000 //12 ADD_OK XOR DATA_3 0000_0010 010_11000 //14 ADD DATA_1 0000_0000 110_11000 //16 STO TEMP 0000_0011 101_11000 //18 LDA DATA_1 0000_0000 010_11000 //1A ADD TEMP 0000_0001 001_00000 //1C SKZ 0000_0000 000_00000 //1E HLT 0000_0000 000_00000 //END HLT 0000_0000 111_00000 //JMP BEGIN 0000_0000test2数据文件:@00 00000001 //1800 DATA_1 10101010 //1801 DATA_2 11111111 //1802 DATA_3 00000000 //1803 TEMPTest3程序TEST3程序是一个计算0~144的斐波那契数列的程序,用来验证CPU整体功能。test3汇编程序:@00 101_11000 //00 LOOP:LDA FN2 0000_0001 110_11000 //02 STO TEMP 0000_0010 010_11000 //04 ADD FN1 0000_0000 110_11000 //06 STO FN2 0000_0001 101_11000 //08 VLDA TEMP 0000_0010 110_11000 //0A STO FN1 0000_0000 100_11000 //0C XOR LIMIT 0000_0011 001_00000 //0E SKZ 0000_0000 111_00000 //10 JMP LOOP 0000_0000 000_00000 //12 DONE HLT 0000_0000test3数据文件:@00 00000001 //1800 FN1 00000000 //1801 FN2 00000000 //1802 TEMP 10010000 //1803 LIMIT完整的testbenchVerilog代码:// Description: RISC-CPU 测试程序 // ----------------------------------------------------------------------------- `include "RISC_CPU.v" `include "ram.v" `include "rom.v" `include "addr_decode.v" `timescale 1ns/1ns `define PERIOD 100 // matches clk_gen.v module cputop_tb; reg [( 3 * 8 ): 0 ] mnemonic; // array that holds 3 8 bits ASCII characters reg [ 12 : 0 ] PC_addr, IR_addr; reg reset_req, clock; wire [ 12 : 0 ] ir_addr, pc_addr; // for post simulation. wire [ 12 : 0 ] addr; wire [ 7 : 0 ] data; wire [ 2 : 0 ] opcode; // for post simulation. wire fetch; // for post simulation. wire rd, wr, halt, ram_sel, rom_sel; integer test; //-----------------DIGITAL LOGIC---------------------- RISC_CPU t_cpu (.clk( clock ),.reset( reset_req ),.halt( halt ),.rd( rd ),.wr( wr ),.addr( addr ),.data( data ),.opcode( opcode ),.fetch( fetch ),.ir_addr( ir_addr ),.pc_addr( pc_addr )); ram t_ram (.addr ( addr [ 9 : 0 ]),.read ( rd ),.write ( wr ),.ena ( ram_sel ),.data ( data )); rom t_rom (.addr ( addr ),.read ( rd ), .ena ( rom_sel ),.data ( data )); addr_decode t_addr_decoder (.addr( addr ),.ram_sel( ram_sel ),.rom_sel( rom_sel )); //-------------------SIMULATION------------------------- initial begin clock = 0; // display time in nanoseconds $timeformat ( -9, 1, "ns", 12 ); display_debug_message; sys_reset; test1; $stop; test2; $stop; test3; $finish; // simulation is finished here. end // initial task display_debug_message; begin $display ("\n************************************************" ); $display ( "* THE FOLLOWING DEBUG TASK ARE AVAILABLE: *" ); $display ( "* \"test1;\" to load the 1st diagnostic program. *"); $display ( "* \"test2;\" to load the 2nd diagnostic program. *"); $display ( "* \"test3;\" to load the Fibonacci program. *"); $display ( "************************************************\n"); end endtask // display_debug_message task test1; begin test = 0; disable MONITOR; $readmemb ("test1.pro", t_rom.memory ); $display ("rom loaded successfully!"); $readmemb ("test1.dat", t_ram.ram ); $display ("ram loaded successfully!"); #1 test = 1; #14800; sys_reset; end endtask // test1 task test2; begin test = 0; disable MONITOR; $readmemb ("test2.pro", t_rom.memory ); $display ("rom loaded successfully!"); $readmemb ("test2.dat", t_ram.ram ); $display ("ram loaded successfully!"); #1 test = 2; #11600; sys_reset; end endtask // test2 task test3; begin test = 0; disable MONITOR; $readmemb ("test3.pro", t_rom.memory ); $display ("rom loaded successfully!"); $readmemb ("test3.dat", t_ram.ram ); $display ("ram loaded successfully!"); #1 test = 3; #94000; sys_reset; end endtask // test1 task sys_reset; begin reset_req = 0; #( `PERIOD * 0.7 ) reset_req = 1; #( 1.5 * `PERIOD ) reset_req = 0; end endtask // sys_reset //--------------------------MONITOR-------------------------------- always@( test ) begin: MONITOR case( test ) 1: begin // display results when running test 1 $display("\n*** RUNNING CPU test 1 - The Basic CPU Diagnostic Program ***"); $display("\n TIME PC INSTR ADDR DATA "); $display(" ------ ---- ------- ------ ------ "); while( test == 1 )@( t_cpu.pc_addr ) begin // fixed if(( t_cpu.pc_addr % 2 == 1 )&&( t_cpu.fetch == 1 )) begin // fixed #60 PC_addr <= t_cpu.pc_addr - 1; IR_addr <= t_cpu.ir_addr; #340 $strobe("%t %h %s %h %h", $time, PC_addr, mnemonic, IR_addr, data ); // Here data has been changed t_cpu.m_register.data end // if t_cpu.pc_addr % 2 == 1 && t_cpu.fetch == 1 end // while test == 1 @ t_cpu.pc_addr end 2: begin // display results when running test 2 $display("\n*** RUNNING CPU test 2 - The Basic CPU Diagnostic Program ***"); $display("\n TIME PC INSTR ADDR DATA "); $display(" ------ ---- ------- ------ ------ "); while( test == 2 )@( t_cpu.pc_addr ) begin // fixed if(( t_cpu.pc_addr % 2 == 1 )&&( t_cpu.fetch == 1 )) begin // fixed #60 PC_addr <= t_cpu.pc_addr - 1; IR_addr <= t_cpu.ir_addr; #340 $strobe("%t %h %s %h %h", $time, PC_addr, mnemonic, IR_addr, data ); // Here data has been changed t_cpu.m_register.data end // if t_cpu.pc_addr % 2 == 1 && t_cpu.fetch == 1 end // while test == 2 @ t_cpu.pc_addr end 3: begin // display results when running test 3 $display("\n*** RUNNING CPU test 3 - An Executable Program **************"); $display("***** This program should calculate the fibonacci *************"); $display("\n TIME FIBONACCI NUMBER "); $display(" ------ -----------------_ "); while( test == 3 ) begin wait( t_cpu.opcode == 3'h 1 ) // display Fib. No. at end of program loop $strobe("%t %d", $time, t_ram.ram [ 10'h 2 ]); wait( t_cpu.opcode != 3'h 1 ); end // while test == 3 end endcase // test end // MONITOR: always@ test //-------------------------HALT------------------------------- always@( posedge halt ) begin // STOP when HALT intruction decoded #500 $display("\n******************************************"); $display( "** A HALT INSTRUCTION WAS PROCESSED !!! **"); $display( "******************************************"); end // always@ posedge halt //-----------------------CLOCK & MNEMONIC------------------------- always#(`PERIOD / 2 ) clock = ~ clock; always@( t_cpu.opcode ) begin // get an ASCII mnemonic for each opcode case( t_cpu.opcode ) 3'b 000 : mnemonic = "HLT"; 3'b 001 : mnemonic = "SKZ"; 3'b 010 : mnemonic = "ADD"; 3'b 011 : mnemonic = "AND"; 3'b 100 : mnemonic = "XOR"; 3'b 101 : mnemonic = "LDA"; 3'b 110 : mnemonic = "STO"; 3'b 111 : mnemonic = "JMP"; default : mnemonic = "???"; endcase end endmodule $ readmemb ( "test1. pro" ,t_ rom. . memory ); $ readmemb ( "testl. dat",t_ ram_ . ram); 即可把编译好的汇编机器码装人虚拟ROM,把需要参加运算的数据装人虚拟RAM就可以开始仿真。上面语句中的第一项为打开的文件名,后一项为系统层次管理下的ROM模块和RAM模块中的存储器memory和ram。源代码&脚本隐藏内容,请前往内页查看详情前仿真结果test1 test2 test3 DC后仿真采用SMIC180工艺在典型环境下进行测试 时序报告: 面积报告: 功耗报告: 综合电路图: 总结该项目更加偏向于教学练习,CPU也是数字IC的重要研究方向,对此感兴趣的同学可以找点论文和开源资料进行学习。可以进一步优化如流水线、运算单元,扩展成SOC系统等。
2023年04月08日
587 阅读
5 评论
5 点赞
2023-04-06
【硬件算法】Verilog之FPGA实现信号移相法
实现信号移相可以用FPGA控制信号0-360度连续可调,对于高频信号(1GHZ以上)超出了FPGA工作频率还有一种办法是FPGA-》DA-》射频前端-》移相器-》阻抗匹配-》天线。本案例直接采用FPGA对数字中频信号处理(kHZ、MHZ)本质是边沿检测与分频现象:代码:/* Function : Phase Shift Interface : clk_fre---unit(MHZ) din_fre---unit(KHz) phase_angle---unit(0-360 Angle) Date: 2023/04/05 Description: Phase shift is carried out on the input square wave. The phase Angle unit is Angle (0-360), or the input can be greater than 360. The system clock unit is MHz and the input signal clock is KHz. */ module PhaseShift( input clk, //clk input rst_n, //rest input [7:0] clk_fre, //system clock frequency,MHZ input [15:0] din_fre, //input signal clock frequency,KHZ input [8:0] phase_angle, //phase shift angle input din, //input signal output reg dout //output signal ); reg [31:0] posedge_counter; reg [31:0] negedge_counter; reg [31:0] delay_counter; reg in_posedge_flg; reg in_negedge_flg; reg out_posedge_flg; reg out_negedge_flg; reg old_din; reg init_data=1'b1; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin posedge_counter <= 1'b0; negedge_counter <= 1'b0; in_posedge_flg <= 1'b0; in_negedge_flg <= 1'b0; out_posedge_flg <= 1'b0; out_negedge_flg <= 1'b0; end else begin if(~old_din & din) in_posedge_flg = 1'b1; if(old_din & ~din) in_negedge_flg = 1'b1; old_din <= din; if(init_data) begin delay_counter <= ((1000000000)/din_fre*clk_fre)/360*(phase_angle%360)/1000000; dout <= din ; init_data <= 1'b0; end if(in_posedge_flg && posedge_counter <= delay_counter) begin posedge_counter <= posedge_counter + 1'b1 ; out_posedge_flg <= 1'b0; end else begin posedge_counter <= 1'b0 ; in_posedge_flg <= 1'b0 ; if(~out_posedge_flg) begin dout <= 1'b1 ; out_posedge_flg <= 1'b1 ; end end if (in_negedge_flg && negedge_counter <= delay_counter) begin negedge_counter <= negedge_counter + 1'b1 ; out_negedge_flg <= 1'b0 ; end else begin negedge_counter <= 1'b0 ; in_negedge_flg <= 1'b0 ; if(~out_negedge_flg) begin dout <= 1'b0 ; out_negedge_flg <= 1'b1; end end end end endmodule测试代码:`timescale 100ps/10ps // module test_PhaseShift; reg clk; reg rst_n; reg [7:0] clk_fre; //system clock frequency,MHZ reg [15:0] din_fre ; //input signal clock frequency,KHZ reg [8:0] phase_angle; //phase shift angle reg din ; //input signal wire dout ; //output signal initial begin clk = 0; din = 0; din_fre = 4000; phase_angle = 90; clk_fre = 100; rst_n = 1; #12 rst_n = 0; #4 rst_n = 1; #10000 $stop; //end end always #5 clk = ~clk; always #125 din = ~din; PhaseShift u1( .clk(clk), .rst_n(rst_n), .clk_fre(clk_fre), .din_fre(din_fre), .phase_angle(phase_angle), .din(din), .dout(dout) ); endmodule
2023年04月06日
620 阅读
0 评论
2 点赞
2023-04-02
【数字IC笔面必备】同步FIFO与异步FIFO
同步FIFO同步FIFO相对异步FIFO较为简单,FIFO:先入先出FIFO原则:满不能写,空不能读。关键:full和empty信号如何产生?方法1:用长度计数器factor。执行一次写操作,factor加1,执行一次读操作,factor减1。方法2:地址位扩展一位,用最高位来判断空满。读地址=写地址,则空;读地址与写地址相差一个存储空间长度,则满。对于方法1来说比较简单,本文重点讲异步FIFO,对于同步FIFO我们给出伪代码,读者自行补全该模块全部代码。异步FIFO异步FIFO的整体结构大致如下:Write_control:控制写操作与满信号(w_full)的判断与产生。Read_control:控制读操作与空信号(r_empty)的判断与产生。RAM:双端口数据存取RAM。Bin_to_gray:二进制码转格雷码模块。用于将读写地址二进制码转成格雷码。SYN:跨时钟同步模块,即将读地址的格雷码(r_g_addr)向w_clk同步;将写地址的格雷码(w_g_addr)向r_clk同步。主要操作就是通过寄存器打两拍。使用扩展地址位来判断空满,读写信号时钟不同。关键在于格雷码使用,同步可以不用格雷码,异步两时钟不一样,采用出错概率大,普通二进制码会出现多个错误,而格雷码每次跳转只会有一位发生变化,出错概率小且顶多是使得FIFO的读或者写操作暂停。详细解释:在中间状态采样,这个是不可能避免的,这是异步系统天生的缺陷。我们的目标是:即使在中间状态采样,也不能影响空满状态的判断。符合这个要求的编码方式是:每次只能有1个bit发生改变。为什么这么说呢?因为当只有一一个bit发生改变时,即使在中间状态采样,其结果也不外乎两种:递增前原指针和递增后新指针。显然递增后新指针是最新情况的反映,如果采样到这个指针,那么和我们的设计预期是一致的,如果采样到递增前的原指针,会有什么结果呢?假设现在采样读指针,那么最坏的情况就是把“不满”判断成了“满”,使得本来被允许的写操作被禁止了,但是这并不会对逻辑产生影响,只是带来了写操作的延迟。同样的,如果现在采样写指针,那么最坏的情况就是把“不空”判断成“空”,使得本来被允许的读操作被禁止了,但是这也不会对逻辑产生影响,只是带来了读操作的延迟显然每次之变化1个bit的编码方案可以有效解决中间态下空满状态的判断问题,格雷码就是这样一种编码。关键点解释1.跨时钟域传递信号做时钟同步一般通过打两拍。2.采用格雷码编码(解决汇聚问题),因为格雷码每次跳转只会有一位发生变化,所以如果出现不确定状态也只会有两种状况,即正确变化了和不变。因此在读写时钟不一样的情况下,纵使读写地址每bit同步过程中出现延时不一致,也不会使得FIFO在实际空或者满之后,FIFO却没有正确的产生出空满信号。只有可能是实际没有空或者满,但产生了空满信号,但这对于FIFO的功能不会有影响,只会使得FIFO的读或者写操作暂停。3.读比写时钟更快,只会只出现实际没满,但误判为满;不会对功能(数据流)造成错误。4.写比读时钟更快,只会出现实际没空,但误判为空;不会对功能(数据流)造成错误。verilog格雷码生成观察下面码表,格雷码最高位与原码一致,其它位对应原码相邻两位相互异或的结果module bin_to_gray #( parameter WIDTH_D = 5 )( input [WIDTH_D-1:0] bin_c, output [WIDTH_D-1:0] gray_c ); wire h_b; assign h_b = bin_c[WIDTH_D-1]; reg [WIDTH_D-2:0] gray_c_d; integer i; always @( * ) for( i=0;i<WIDTH_D-1;i=i+1 ) gray_c_d[i] = bin_c[i]^bin_c[i+1]; assign gray_c = ; endmodule源程序下载
2023年04月02日
228 阅读
0 评论
2 点赞
3~4月的花花,浅浅的记录一下~
最近在长安区遇到的3~4月的花花,浅浅的记录一下~
2023年04月02日
82 阅读
0 评论
3 点赞
2023-03-27
电子信息类领域情诗
你是我心中的NE555你让我充满了电流和电压你是我灵魂的数字芯片你让我拥有了逻辑和智慧你的眼睛如LED般光彩你的微笑如LCD般温暖你的声音如DAC般动听你的气息如FPGA般灵活我想和你一起创造电路、程序、系统、应用我想和你一起分享数据、信号、信息、感情你是我的输入和输出你是我的模拟和数字你是我的并行和串行你是我的高频和低频
2023年03月27日
188 阅读
0 评论
2 点赞
2023-03-21
IC/SOC封装类型与对应成本关系
根据引脚引出的不同,封装可分为外围和阵列封装。根据有无封装连线分为有连接线的( Wire Bond)和倒装片(Flip-chip) 封装。其中,外围封装形式有DIP、PLCC、QFP、SOP等,如图14-23所示。此类封装的优点是价格便宜,板级可靠性比较高。但是其电性能和热性能较差,并且受到I/O引脚数目的限制,如PQFP (Plastic Quad Flat Package,塑料四边引脚扁平封装),I/O 引脚数目也只能达到208~240个。阵列封装包括有连线的BGA和无连线的Flip-chip BGA等,如图14-24 所示。此类封装的优点是I/O密度高,板级集成的成品率高,但是价格相对较高。(1) BGA封装BGA封装技术的出现是封装技术的一大突破,它是近几年来推动封装市场的强有力的技术之一,BGA封装一改传统的封装结构,将引线从封装基板的底部以阵列球的方式引出,这样不仅可以安排更多的IO,而且大大提高了封装密度,改进了电性能。如果它再采用MCP (多芯片模块)封装或倒装片技术,有望进一步提高产 品的速度和降低复杂性。目前, BGA封装按照基板的种类,主要分为PBGA (塑封BGA)、CBGA (陶瓷BGA)、TBGA(载带BGA)、MBGA (金属BGA)等。图14-25以PBGA为例进行说明,PBGA中的焊球做在PWB基板上,基板是BT多层布线基板(2~4层),封装采用的焊球材料为共晶或准共晶Pb-Sn合金、焊球和封装体的连接不需要另外的焊料。(2)倒装片封装(Flip Chip Packages)倒装片技术是一种先进的,非常有前途的集成电路封装技术。封装倒装片是一一种由 IBM公司最先使用的先进封装技术,它是利用倒装技术将芯片直接装入一- 个封装体内。倒装片封装可以是单芯片也可以是多芯片形式,其发展历史已将近40年,主要在手持或移动电子产品中使用广泛。一般芯片都是面朝上安装互连,而此类技术则是芯片面朝下,芯片上的焊区直接与基板上的焊区互连,如图14-26所示。因此,倒装封装的互连线非常短,由互连线产生的电容、电阻和电感也比其他封装形式小得多,具有较高的电性能和热性能。此外,采用此类封装的芯片焊区可面阵布局,更适于多I/O数的VLSI芯片使用。当然,倒装技术也有不足之处,如芯片面朝下安装互连,给工艺操作带来一定难度一焊 点检查困难,板级可靠性需要解决,费用也偏高。(3)多芯片封装(MCP, Multi Chip Package)多芯片封装是20世纪90年代兴起的- -种混合微电子组装技术,它是在高密度多层布线基板上,将若干裸芯片IC组装和互连,构成更复杂的或具有子系统功能的高级电子组件,常见的有Flash+MCU、Flash+MCU+SRAM、 SRAM+MCU和Analog IC+Digital IC+Memory等组合。图14-27 所示为MCP封装的示意图。目前,MCP多采用IC芯片叠层放置,可大大节约基板的面积,其主要特点是布线密度高,互连线短,体积小,重量轻和性能高等。但是由于封装了多块芯片,使得良品率有所下降,并且测试相对较困难,测试成本也很高。
2023年03月21日
331 阅读
0 评论
2 点赞
2023-03-18
【优惠活动】25考研西北大学电子信息类专业全程班
所有课程https://edu.ee.ac.cn/25全程班(学习通上课):https://mooc1.chaoxing.com/course/218652689.html课程二维码:奖品内容📚 一等奖奖优惠200元 💻 二等奖优惠150元 📱 三等奖优惠100元 📖 四等奖优惠80元 📖 五等奖优惠50元 只要参与98%概率中奖任何一个 活动内容点开下面2个网页任何一个都可,然后点击分享到QQ空间或微信朋友圈,配上文案:西北大学843电子信息类考研全程班,有需要可以了解,QQ群:519462257。 任选一个 网页一https://mooc1.chaoxing.com/mooc-ans/course/218652689.html网页二https://edu.ee.ac.cn/兑奖条件1、转发后需要获得至少5个人的点赞2、转发后3天内不可删除转发消息,否则取消兑奖资格3、完成后凭截图参与抽奖声明&收奖方式满足活动条件后请将截图发至以下任一核实地方1、截图发至QQ:2091645818/Wechat:eecslab2、截图上传至评论区发送上面二个任意一个都可以,核实后奖品会立刻发放每人仅能参与1次,中奖额度与赞数无关,达到最低要求即可西北大学25考研班:包含数电、模电、 电路 三科知识点讲解、划重点、送初复试资料、真题模拟题带刷带练、 点对点答疑 服务拟定课时不低于80课时,课时更新中课程链接: 赠送资料免费笔记入口
2023年03月18日
1,019 阅读
17 评论
7 点赞
使用Verilog实现与仿真AMBA--AHB总线协议(三)
1、AHB从机AHB从机应答来自系统主主机发起的传输。从机使用从译码器输出的 HSELx 信号来决定它什么时候作应答。其它传输需要的信号,如地址与控制信息由主机产生。下图为一个AHB从机的接口框图:下面是一个AHB接口的SRAM控制器的程序://------------------------------------------------------------------------------ // File : ahb_sram.v // Editor : VSCode, Tab Size(4) // Description : Top Module of AHB SRAM Controller. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module ahb_sram #( parameter SYNC_RESET = 1, parameter AHB_DWIDTH = 32, parameter AHB_AWIDTH = 32, parameter SIZE_IN_BYTES = 2048, parameter ADD_WIDTH = $clog2(SIZE_IN_BYTES) ) ( //---------------------------------- // IO Declarations //---------------------------------- // Inputs input HCLK, input HRESETN, input HSEL, input HREADYIN, input [1:0] HTRANS, input [2:0] HBURST, input [2:0] HSIZE, input [AHB_DWIDTH-1:0] HWDATA, input [AHB_AWIDTH-1:0] HADDR, input HWRITE, // Outputs output [AHB_DWIDTH-1:0] HRDATA, output [1:0] HRESP, output HREADYOUT ); //---------------------------------- // Variable Declarations //---------------------------------- wire [ADD_WIDTH-1:0] HADDR_cal; wire [2:0] ahbsram_size; wire [ADD_WIDTH-1:0] ahbsram_addr; wire [31:0] ahbsram_wdata; wire ahbsram_write; wire [31:0] sramahb_rdata; wire sramahb_ack; //---------------------------------- // Start of Main Code //---------------------------------- assign HADDR_cal = HADDR[ADD_WIDTH-1:0]; // Instantiations ahb_sram_if #( .AHB_DWIDTH (AHB_DWIDTH), .AHB_AWIDTH (AHB_AWIDTH), .ADD_WIDTH (ADD_WIDTH), .SYNC_RESET (SYNC_RESET) ) u_ahb_sram_if ( .HCLK (HCLK), .HRESETN (HRESETN), .HSEL (HSEL), .HTRANS (HTRANS), .HBURST (HBURST), .HWRITE (HWRITE), .HWDATA (HWDATA), .HSIZE (HSIZE), .HADDR (HADDR_cal), .HREADYIN (HREADYIN), // From SRAM Control signals .sramahb_ack (sramahb_ack), .sramahb_rdata (sramahb_rdata), // Outputs .HREADYOUT (HREADYOUT), .HRESP (HRESP), // To SRAM Control signals .ahbsram_req (ahbsram_req), .ahbsram_write (ahbsram_write), .ahbsram_wdata (ahbsram_wdata), .ahbsram_size (ahbsram_size), .ahbsram_addr (ahbsram_addr), .HRDATA (HRDATA) ); sram_ctrl_if #( .ADD_WIDTH (ADD_WIDTH), .SYNC_RESET (SYNC_RESET) ) u_sram_ctrl_if ( .HCLK (HCLK), .HRESETN (HRESETN), // From AHB Interface signals .ahbsram_req (ahbsram_req), .ahbsram_write (ahbsram_write), .ahbsram_wdata (ahbsram_wdata), .ahbsram_size (ahbsram_size), .ahbsram_addr (ahbsram_addr), // Outputs // To AHB Interface signals .sramahb_ack (sramahb_ack), .sramahb_rdata (sramahb_rdata) ); endmodule //------------------------------------------------------------------------------ // File : ahb_sram_if.v // Editor : VSCode, Tab Size(4) // Description : // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module ahb_sram_if #( parameter AHB_DWIDTH = 32, parameter AHB_AWIDTH = 32, parameter ADD_WIDTH = 11, parameter SYNC_RESET = 0 ) ( //---------------------------------- // IO Declarations //---------------------------------- // Inputs input HCLK, input HRESETN, input HSEL, input HREADYIN, input [1:0] HTRANS, input [2:0] HBURST, input [2:0] HSIZE, input [ADD_WIDTH-1:0] HADDR, input [AHB_DWIDTH-1:0] HWDATA, input HWRITE, input sramahb_ack, input [AHB_DWIDTH-1:0] sramahb_rdata, // Outputs output HREADYOUT, output [1:0] HRESP, output reg [AHB_DWIDTH-1:0] HRDATA, output ahbsram_req, output ahbsram_write, output [AHB_AWIDTH-1:0] ahbsram_wdata, output [2:0] ahbsram_size, output [ADD_WIDTH-1:0] ahbsram_addr ); //---------------------------------- // Local Parameter Declarations //---------------------------------- // State Machine parameters localparam IDLE = 2'b00; localparam AHB_WR = 2'b01; localparam AHB_RD = 2'b10; parameter RESP_OKAY = 2'b00; parameter RESP_ERROR = 2'b01; // AHB HTRANS definition parameter TRN_IDLE = 2'b00; parameter TRN_BUSY = 2'b01; parameter TRN_SEQ = 2'b11; parameter TRN_NONSEQ = 2'b10; parameter SINGLE = 3'b000; parameter INCR = 3'b001; parameter WRAP4 = 3'b010; parameter INCR4 = 3'b011; parameter WRAP8 = 3'b100; parameter INCR8 = 3'b101; parameter WRAP16 = 3'b110; parameter INCR16 = 3'b111; //---------------------------------- // Variable Declarations //---------------------------------- reg [1:0] HTRANS_d; reg [2:0] HBURST_d; reg [2:0] HSIZE_d; reg [ADD_WIDTH-1:0] HADDR_d; reg [AHB_DWIDTH-1:0] HWDATA_d; reg HWRITE_d; reg HSEL_d; reg HREADYIN_d; reg [1:0] ahbcurr_state; reg [1:0] ahbnext_state; reg latchahbcmd; reg ahbsram_req_int; reg ahbsram_req_d1; reg [AHB_DWIDTH-1:0] HWDATA_cal; reg [4:0] burst_count; reg [4:0] burst_count_reg; reg [4:0] count; wire aresetn; wire sresetn; //---------------------------------- // Start of Main Code //---------------------------------- assign aresetn = (SYNC_RESET==1) ? 1'b1 : HRESETN; assign sresetn = (SYNC_RESET==1) ? HRESETN : 1'b1; // Generation of valid AHB Command which triggers the AHB Slave State Machine assign validahbcmd = HREADYIN & HSEL & (HTRANS == TRN_NONSEQ); // Generation of HRESP assign HRESP = RESP_OKAY; always @(*) begin HWDATA_cal = HWDATA; end // Latch all the AHB signals always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin HADDR_d <= }; HWDATA_d <= }; HTRANS_d <= 2'b00; HSIZE_d <= 2'b00; HBURST_d <= 3'b000; HWRITE_d <= 1'b0; HSEL_d <= 1'b0; HREADYIN_d <= 1'b0; end else if (HREADYIN == 1'b1 & HSEL == 1'b1 & HREADYOUT == 1'b1) begin HADDR_d <= HADDR; HTRANS_d <= HTRANS; HSIZE_d <= HSIZE; HBURST_d <= HBURST; HWRITE_d <= HWRITE; HWDATA_d <= HWDATA_cal; HSEL_d <= HSEL; HREADYIN_d <= HREADYIN; end end // Current State generation always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin ahbcurr_state <= IDLE; end else begin ahbcurr_state <= ahbnext_state; end end // Next State and output decoder logic always @(*) begin latchahbcmd = 1'b0; ahbsram_req_int = 1'b0; ahbnext_state = ahbcurr_state; case (ahbcurr_state) IDLE : begin if (HREADYIN == 1'b1 && HSEL == 1'b1 && ((HTRANS == TRN_NONSEQ) || HTRANS == TRN_SEQ)) begin latchahbcmd = 1'b1; if (HWRITE == 1'b1) begin ahbnext_state = AHB_WR; end else begin ahbnext_state = AHB_RD; end end else begin ahbnext_state = IDLE; end end AHB_WR : begin latchahbcmd = 1'b0; ahbsram_req_int = 1'b1; if (sramahb_ack == 1'b1) begin if (count == burst_count_reg) begin ahbnext_state = IDLE; end else begin ahbsram_req_int = 1'b0; end end end AHB_RD : begin latchahbcmd = 1'b0; ahbsram_req_int = 1'b1; if (sramahb_ack == 1'b1) begin ahbnext_state = IDLE; end end default : begin ahbnext_state = IDLE; end endcase end // LOGIC FOR BURST COUNT always @(*) begin burst_count = burst_count_reg; if (HSEL == 1'b1 && HTRANS == TRN_NONSEQ && HREADYIN == 1'b1 && HREADYOUT == 1'b1) begin case (HBURST) SINGLE : burst_count = 5'b00001; WRAP4,INCR4 : burst_count = 5'b00100; WRAP8,INCR8 : burst_count = 5'b01000; WRAP16,INCR16 : burst_count = 5'b10000; default : burst_count = 4'b0001; endcase end end always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin burst_count_reg <= 'h0; end else begin burst_count_reg <= burst_count; end end always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin count <= 5'h0; end else begin if (count == burst_count_reg) begin count <= 5'h0; end else if (ahbsram_req == 1'b1) begin count <= count + 1'b1; end else begin count <= count; end end end assign HREADYOUT = !ahbsram_req_int; // Generation of signals required for SRAM assign ahbsram_write = ahbsram_req ? HWRITE_d : 1'b0; assign ahbsram_wdata = HWDATA; assign ahbsram_addr = ahbsram_req ? HADDR_d : HADDR_d; assign ahbsram_size = ahbsram_req ? HSIZE_d : HSIZE_d; always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin ahbsram_req_d1 <= 1'b0; end else begin ahbsram_req_d1 <= ahbsram_req_int; end end // Generate the request to the SRAM contol logic when there is AHB read or write request assign ahbsram_req = ahbsram_req_int & !ahbsram_req_d1; // HRDATA generation always @(*) begin if (HREADYOUT && HREADYIN) begin HRDATA = sramahb_rdata; end else begin HRDATA = sramahb_rdata; end end endmodule //------------------------------------------------------------------------------ // File : sram_ctrl_if.v // Editor : VSCode, Tab Size(4) // Description : // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module sram_ctrl_if #( parameter AHB_DWIDTH = 32, parameter ADD_WIDTH = 11, parameter SYNC_RESET = 0 ) ( //---------------------------------- // IO Declarations //---------------------------------- // Inputs input HCLK, input HRESETN, input ahbsram_req, input ahbsram_write, input [2:0] ahbsram_size, input [ADD_WIDTH-1:0] ahbsram_addr, input [AHB_DWIDTH-1:0] ahbsram_wdata, // Outputs output sramahb_ack, output reg [AHB_DWIDTH-1: 0] sramahb_rdata ); //---------------------------------- // Local Parameter Declarations //---------------------------------- // State Machine parameters localparam S_IDLE = 2'b00; localparam S_WR = 2'b01; localparam S_RD = 2'b10; //---------------------------------- // Variable Declarations //---------------------------------- reg [3:0] sram_wen_mem; reg [1:0] sramcurr_state; reg [1:0] sramnext_state; reg sram_wen; reg sram_ren; reg sramahb_ack_int; reg sram_ren_d; reg sram_done; wire [AHB_DWIDTH-1:0] ram_rdata; wire aresetn; wire sresetn; //---------------------------------- // Start of Main Code //---------------------------------- assign aresetn = (SYNC_RESET == 1) ? 1'b1 : HRESETN; assign sresetn = (SYNC_RESET == 1) ? HRESETN : 1'b1; // Current State generation always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sramcurr_state <= S_IDLE; end else begin sramcurr_state <= sramnext_state; end end // Next State and output decoder logic always @(*) begin sramahb_ack_int = 1'b0; sram_wen = 1'b0; sram_ren = 1'b0; sramnext_state = sramcurr_state; case (sramcurr_state) S_IDLE : begin if (ahbsram_req == 1'b1) begin if (ahbsram_write == 1'b1) begin sramnext_state = S_WR; sram_wen = 1'b1; end else begin sram_ren = 1'b1; sramnext_state = S_RD; end end end S_WR : begin if (sram_done == 1'b1) begin sramnext_state = S_IDLE; sramahb_ack_int = 1'b1; end end S_RD : begin if (sram_done == 1'b1) begin sramnext_state = S_IDLE; sramahb_ack_int = 1'b1; end end default : begin sramnext_state = S_IDLE; end endcase end always @(*) begin sram_wen_mem = 4'b0000; if (ahbsram_size == 3'b010) begin sram_wen_mem = }; end else if (ahbsram_size == 3'b001) begin case (ahbsram_addr[1]) 1'b0 : begin sram_wen_mem[0] = sram_wen; sram_wen_mem[1] = sram_wen; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = 1'b0; end 1'b1 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = sram_wen; sram_wen_mem[3] = sram_wen; end endcase end else if (ahbsram_size == 3'b000) begin case (ahbsram_addr[1:0]) 2'b00 : begin sram_wen_mem[0] = sram_wen; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = 1'b0; end 2'b01 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = sram_wen; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = 1'b0; end 2'b10 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = sram_wen; sram_wen_mem[3] = 1'b0; end 2'b11 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = sram_wen; end endcase end else begin sram_wen_mem = }; end end // SRAM Instantiations sram_model #( .SYNC_RESET (SYNC_RESET), .ADDR_WIDTH (ADD_WIDTH) ) u_sram_model ( .writedata (ahbsram_wdata), .readdata (ram_rdata[31:0]), .wren (sram_wen_mem), .rden (sram_ren), .writeaddr (ahbsram_addr[ADD_WIDTH-1:2]), .readaddr (ahbsram_addr[ADD_WIDTH-1:2]), .clk (HCLK), .resetn (HRESETN) ); always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sramahb_rdata <= 32'h0; end else if (sram_ren_d == 1'b1) begin sramahb_rdata <= ram_rdata; end else begin sramahb_rdata <= sramahb_rdata; end end always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sram_ren_d <= 32'h0; end else begin sram_ren_d <= sram_ren; end end // Generate the SRAM done when the SRAM wren/rden is done always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sram_done <= 1'b0; end else if (sram_wen || sram_ren) begin sram_done <= 1'b1; end else begin sram_done <= 1'b0; end end // Generate the SRAM ack assign sramahb_ack = sramahb_ack_int; endmodule //------------------------------------------------------------------------------ // File : sram_model.v // Editor : GVIM, Tab Size(4) // Description : FPGA Block Ram/Onchip SRAM. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module sram_model #( parameter SYNC_RESET = 0, parameter ADDR_WIDTH = 16 ) ( //---------------------------------- // IO Declarations //---------------------------------- input [31:0] writedata, output [31:0] readdata, input [3:0] wren, input rden, input [ADDR_WIDTH-1:2] writeaddr, input [ADDR_WIDTH-1:2] readaddr, input clk, input resetn ); //---------------------------------- //--Local Parameter Declarations //---------------------------------- localparam AWT = ((1<<(ADDR_WIDTH-2))-1); //---------------------------------- // Variable Declarations //---------------------------------- reg [7:0] bram0[AWT:0]; reg [7:0] bram1[AWT:0]; reg [7:0] bram2[AWT:0]; reg [7:0] bram3[AWT:0]; reg [ADDR_WIDTH-3:0] addr_q1; reg rden_r = 1'b0; wire [31:0] readdata_i; wire aresetn; wire sresetn; //---------------------------------- // Start of Main Code //---------------------------------- assign aresetn = (SYNC_RESET == 1) ? 1'b1 : resetn; assign sresetn = (SYNC_RESET == 1) ? resetn : 1'b1; always @(posedge clk) begin rden_r <= rden; end // Infer Block RAM always @(posedge clk) begin if (wren[0]) bram0[writeaddr] <= writedata[7:0]; if (wren[1]) bram1[writeaddr] <= writedata[15:8]; if (wren[2]) bram2[writeaddr] <= writedata[23:16]; if (wren[3]) bram3[writeaddr] <= writedata[31:24]; end always @(posedge clk) begin addr_q1 <= readaddr[ADDR_WIDTH-1:2]; end assign readdata_i = ; assign readdata = rden_r ? readdata_i : }; endmodule下面是测试用例代码://------------------------------------------------------------------------------ // File : top_tb.v // Editor : VSCode, Tab Size(4) // Description : Testbench of AHB SRAM Controller. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module top_tb(); //---------------------------------- // Local Parameter Declarations //---------------------------------- localparam AHB_CLK_PERIOD = 5; // Assuming AHB CLK to be 100MHz localparam SIZE_IN_BYTES =2048; //---------------------------------- // Variable Declarations //---------------------------------- reg HCLK = 0; wire HWRITE; wire [1:0] HTRANS; wire [2:0] HSIZE; wire [2:0] HBURST; wire HREADYIN; wire [31:0] HADDR; wire [31:0] HWDATA; wire HREADYOUT; wire [1:0] HRESP; wire [31:0] HRDATA; reg HRESETn; //---------------------------------- // Start of Main Code //---------------------------------- //----------------------------------------------------------------------- // Generate HCLK //----------------------------------------------------------------------- always #AHB_CLK_PERIOD HCLK <= ~HCLK; //----------------------------------------------------------------------- // Generate HRESETn //----------------------------------------------------------------------- initial begin HRESETn = 1'b0; repeat(5) @(posedge HCLK); HRESETn = 1'b1; end ahb_master #( .START_ADDR (32'h0), .DEPTH_IN_BYTES (SIZE_IN_BYTES) ) u_ahb_master ( .HRESETn (HRESETn), .HCLK (HCLK), .HADDR (HADDR), .HTRANS (HTRANS), .HWRITE (HWRITE), .HSIZE (HSIZE), .HBURST (HBURST), .HWDATA (HWDATA), .HRDATA (HRDATA), .HRESP (HRESP), .HREADY (HREADYOUT) ); ahb_sram # ( .AHB_AWIDTH (32), .AHB_DWIDTH (32), .SIZE_IN_BYTES (SIZE_IN_BYTES), .SYNC_RESET (1) ) u_ahb_sram ( .HCLK (HCLK), .HRESETN (HRESETn), .HSEL (1'b1), .HWRITE (HWRITE), .HADDR (HADDR), .HWDATA (HWDATA), .HRDATA (HRDATA), .HSIZE (HSIZE), .HTRANS (HTRANS), .HBURST (HBURST), .HRESP (HRESP), .HREADYIN (1'b1), .HREADYOUT (HREADYOUT) ); `ifdef VCS initial begin $fsdbDumpfile("top_tb.fsdb"); $fsdbDumpvars; end initial begin `ifdef DUMP_VPD $vcdpluson(); `endif end `endif endmodule这个模块主要是产生了时钟和复位信号,例化了待测模块,以及一个AHB Master的功能模型。具体见下面的代码://------------------------------------------------------------------------------ // File : ahb_master.v // Editor : VSCode, Tab Size(4) // Description : AHB Master Simulation Model. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps `define SINGLE_TEST `define BURST_TEST module ahb_master #( //---------------------------------- // Paramter Declarations //---------------------------------- parameter START_ADDR = 0, parameter DEPTH_IN_BYTES = 32'h100, parameter END_ADDR = START_ADDR+DEPTH_IN_BYTES-1 ) ( //---------------------------------- // IO Declarations //---------------------------------- input wire HRESETn, input wire HCLK, output reg [31:0] HADDR, output reg [1:0] HTRANS, output reg HWRITE, output reg [2:0] HSIZE, output reg [2:0] HBURST, output reg [31:0] HWDATA, input wire [31:0] HRDATA, input wire [1:0] HRESP, input wire HREADY ); //---------------------------------- // Variable Declarations //---------------------------------- reg [31:0] data_burst[0:1023]; //---------------------------------- // Start of Main Code //---------------------------------- initial begin HADDR = 0; HTRANS = 0; HWRITE = 0; HSIZE = 0; HBURST = 0; HWDATA = 0; while(HRESETn === 1'bx) @(posedge HCLK); while(HRESETn === 1'b1) @(posedge HCLK); while(HRESETn === 1'b0) @(posedge HCLK); `ifdef SINGLE_TEST repeat(3) @(posedge HCLK); memory_test(START_ADDR, END_ADDR, 1); memory_test(START_ADDR, END_ADDR, 2); memory_test(START_ADDR, END_ADDR, 4); `endif `ifdef BURST_TEST repeat(5) @(posedge HCLK); memory_test_burst(START_ADDR, END_ADDR, 1); memory_test_burst(START_ADDR, END_ADDR, 2); memory_test_burst(START_ADDR, END_ADDR, 4); memory_test_burst(START_ADDR, END_ADDR, 6); memory_test_burst(START_ADDR, END_ADDR, 8); memory_test_burst(START_ADDR, END_ADDR, 10); memory_test_burst(START_ADDR, END_ADDR, 16); memory_test_burst(START_ADDR, END_ADDR, 32); memory_test_burst(START_ADDR, END_ADDR, 64); memory_test_burst(START_ADDR, END_ADDR, 128); memory_test_burst(START_ADDR, END_ADDR, 255); repeat(5) @(posedge HCLK); `endif $finish(2); end //----------------------------------------------------------------------- // Single transfer test //----------------------------------------------------------------------- task memory_test; input [31:0] start; // start address input [31:0] finish; // end address input [2:0] size; // data size: 1, 2, 4 integer i; integer error; reg [31:0] data; reg [31:0] gen; reg [31:0] got; reg [31:0] reposit[START_ADDR:END_ADDR]; begin $display("%m: read-after-write test with %d-byte access", size); error = 0; gen = $random(7); for (i = start; i < (finish-size+1); i = i + size) begin gen = $random & ~32'b0; data = align(i, gen, size); ahb_write(i, size, data); ahb_read(i, size, got); got = align(i, got, size); if (got !== data) begin $display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data); error = error + 1; end end if (error == 0) $display("[%10d] %m OK: from %x to %x", $time, start, finish); $display("%m read-all-after-write-all with %d-byte access", size); error = 0; gen = $random(1); for (i = start; i < (finish-size+1); i = i + size) begin gen = & ~32'b0; data = align(i, gen, size); reposit[i] = data; ahb_write(i, size, data); end for (i = start; i < (finish-size+1); i = i + size) begin data = reposit[i]; ahb_read(i, size, got); got = align(i, got, size); if (got !== data) begin $display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data); error = error + 1; end end if (error == 0) $display("[%10d] %m OK: from %x to %x", $time, start, finish); end endtask //----------------------------------------------------------------------- // Burst transfer test //----------------------------------------------------------------------- task memory_test_burst; input [31:0] start; // start address input [31:0] finish; // end address input [7:0] leng; // burst length integer i; integer j; integer k; integer r; integer error; reg [31:0] data; reg [31:0] gen; reg [31:0] got; reg [31:0] reposit[0:1023]; integer seed; begin $display("%m: read-all-after-write-all burst test with %d-beat access", leng); error = 0; seed = 111; gen = $random(seed); k = 0; if (finish > (start+leng*4)) begin for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin for (j = 0; j < leng; j = j + 1) begin data_burst[j] = $random; reposit[j+k*leng] = data_burst[j]; end @(posedge HCLK); ahb_write_burst(i, leng); k = k + 1; end gen = $random(seed); k = 0; for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin @(posedge HCLK); ahb_read_burst(i, leng); for (j = 0; j < leng; j = j + 1) begin if (data_burst[j] != reposit[j+k*leng]) begin error = error+1; $display("%m A=%hh D=%hh, but %hh expected",i+j*leng, data_burst[j], reposit[j+k*leng]); end end k = k + 1; r = $random & 8'h0F; repeat(r) @(posedge HCLK); end if (error == 0) $display("%m %d-length burst read-after-write OK: from %hh to %hh",leng, start, finish); end else begin $display("%m %d-length burst read-after-write from %hh to %hh ???",leng, start, finish); end end endtask //----------------------------------------------------------------------- // As AMBA AHB bus uses non-justified data bus scheme, data should be // aligned according to the address. //----------------------------------------------------------------------- function [31:0] align; input [ 1:0] addr; input [31:0] data; input [ 2:0] size; // num of bytes begin `ifdef BIG_ENDIAN case (size) 1 : case (addr[1:0]) 0 : align = data & 32'hFF00_0000; 1 : align = data & 32'h00FF_0000; 2 : align = data & 32'h0000_FF00; 3 : align = data & 32'h0000_00FF; endcase 2 : case (addr[1]) 0 : align = data & 32'hFFFF_0000; 1 : align = data & 32'h0000_FFFF; endcase 4 : align = data&32'hFFFF_FFFF; default : $display($time,,"%m ERROR %d-byte not supported for size", size); endcase `else case (size) 1 : case (addr[1:0]) 0 : align = data & 32'h0000_00FF; 1 : align = data & 32'h0000_FF00; 2 : align = data & 32'h00FF_0000; 3 : align = data & 32'hFF00_0000; endcase 2 : case (addr[1]) 0 : align = data & 32'h0000_FFFF; 1 : align = data & 32'hFFFF_0000; endcase 4 : align = data&32'hFFFF_FFFF; default : $display($time,,"%m ERROR %d-byte not supported for size", size); endcase `endif end endfunction `include "ahb_transaction_tasks.v" endmodule这个模块主要是实现了两个testcase,一个是测试single传输,另一个测试burst传输。AHB读写传输任务被封装在ahb_transaction_tasks.v文件中://------------------------------------------------------------------------------ // File : ahb_transaction_tasks.v // Editor : VSCode, Tab Size(4) // Description : AHB Transaction Tasks. // //------------------------------------------------------------------------------ `ifndef __AHB_TRANSACTION_TASKS_V__ `define __AHB_TRANSACTION_TASKS_V__ //----------------------------------------------------------------------- // AHB Read Task //----------------------------------------------------------------------- task ahb_read; input [31:0] address; input [2:0] size; output [31:0] data; begin @(posedge HCLK); HADDR <= #1 address; HTRANS <= #1 2'b10; // NONSEQ; HBURST <= #1 3'b000; // SINGLE; HWRITE <= #1 1'b0; // READ; case (size) 1 : HSIZE <= #1 3'b000; // BYTE; 2 : HSIZE <= #1 3'b001; // HWORD; 4 : HSIZE <= #1 3'b010; // WORD; default : $display($time,, "ERROR: unsupported transfer size: %d-byte", size); endcase @(posedge HCLK); while (HREADY !== 1'b1) @(posedge HCLK); HTRANS <= #1 2'b0; // IDLE @(posedge HCLK); while (HREADY === 0) @(posedge HCLK); data = HRDATA; // must be blocking if (HRESP != 2'b00) $display($time,, "ERROR: non OK response for read"); @(posedge HCLK); end endtask //----------------------------------------------------------------------- // AHB Write Task //----------------------------------------------------------------------- task ahb_write; input [31:0] address; input [2:0] size; input [31:0] data; begin @(posedge HCLK); HADDR <= #1 address; HTRANS <= #1 2'b10; // NONSEQ HBURST <= #1 3'b000; // SINGLE HWRITE <= #1 1'b1; // WRITE case (size) 1 : HSIZE <= #1 3'b000; // BYTE 2 : HSIZE <= #1 3'b001; // HWORD 4 : HSIZE <= #1 3'b010; // WORD default : $display($time,, "ERROR: unsupported transfer size: %d-byte", size); endcase @(posedge HCLK); while (HREADY !== 1) @(posedge HCLK); HWDATA <= #1 data; HTRANS <= #1 2'b0; // IDLE @(posedge HCLK); while (HREADY === 0) @(posedge HCLK); if (HRESP != 2'b00) $display($time,, "ERROR: non OK response write"); @(posedge HCLK); end endtask //----------------------------------------------------------------------- // AHB Read Burst Task //----------------------------------------------------------------------- task ahb_read_burst; input [31:0] addr; input [31:0] leng; integer i; integer ln; integer k; begin k = 0; @(posedge HCLK); HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b10; // NONSEQ if (leng >= 16) begin HBURST <= #1 3'b111; // INCR16 ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8 ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4 ln = 4; end else begin HBURST <= #1 3'b001; // INCR ln = leng; end HWRITE <= #1 1'b0; // READ HSIZE <= #1 3'b010; // WORD @(posedge HCLK); while (HREADY == 1'b0) @(posedge HCLK); while (leng > 0) begin for (i = 0; i < ln-1; i = i + 1) begin HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b11; // SEQ; @(posedge HCLK); while (HREADY == 1'b0) @(posedge HCLK); data_burst[k%1024] <= HRDATA; k = k + 1; end leng = leng - ln; if (leng == 0) begin HADDR <= #1 0; HTRANS <= #1 0; HBURST <= #1 0; HWRITE <= #1 0; HSIZE <= #1 0; end else begin HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b10; // NONSEQ if (leng >= 16) begin HBURST <= #1 3'b111; // INCR16 ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8 ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4 ln = 4; end else begin HBURST <= #1 3'b001; // INCR1 ln = leng; end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); data_burst[k%1024] = HRDATA; // must be blocking k = k + 1; end end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); data_burst[k%1024] = HRDATA; // must be blocking end endtask //----------------------------------------------------------------------- // AHB Write Burst Task // It takes suitable burst first and then incremental. //----------------------------------------------------------------------- task ahb_write_burst; input [31:0] addr; input [31:0] leng; integer i; integer j; integer ln; begin j = 0; ln = 0; @(posedge HCLK); while (leng > 0) begin HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b10; // NONSEQ if (leng >= 16) begin HBURST <= #1 3'b111; // INCR16 ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8 ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4 ln = 4; end else begin HBURST <= #1 3'b001; // INCR ln = leng; end HWRITE <= #1 1'b1; // WRITE HSIZE <= #1 3'b010; // WORD for (i = 0; i < ln-1; i = i + 1) begin @(posedge HCLK); while (HREADY == 1'b0) @(posedge HCLK); HWDATA <= #1 data_burst[(j+i)%1024]; HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b11; // SEQ; while (HREADY == 1'b0) @(posedge HCLK); end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); HWDATA <= #1 data_burst[(j+i)%1024]; if (ln == leng) begin HADDR <= #1 0; HTRANS <= #1 0; HBURST <= #1 0; HWRITE <= #1 0; HSIZE <= #1 0; end leng = leng - ln; j = j + ln; end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); if (HRESP != 2'b00) begin // OKAY $display($time,, "ERROR: non OK response write"); end `ifdef DEBUG $display($time,, "INFO: write(%x, %d, %x)", addr, size, data); `endif HWDATA <= #1 0; @(posedge HCLK); end endtask `endif使用VCS仿真,打印信息如下:整体仿真波形如下:不喜欢用DVE的话,也可以选择用Verdi查看波形,这种比较简单的调试,DVE也足够用了。下面贴一张突发传输的细节图:
2023年03月16日
178 阅读
0 评论
1 点赞
2023-03-13
Verilog-位宽计算的系统函数$clog2
一、什么是$clog2clog2 这是一个系统函数,第一次出现于Verilog-2005版本中,在IEEE中处在17.11.1节的math functions中,因为log2是2进制的对数,所以这个系统函数在电路设计的计算位宽时体现出了自身的方便性,需要注意的是,这里的$clog2是向上取整的一个系统函数,比如二、$clog2的优势和案例在老的IEEE verilog版本中,假如不用clog2去计算位宽,我们可能需要如下的function函数来进行位宽计算,这个函数本身很好理解,即通过移位去检测depth的位宽,之后我们需要再将计算得到的数字使用在端口定义的过程中。function integer clog2( input integer depth ); begin if(depth == 0) clog2 = 1; else if(depth != 0) for(clog2 = 0; depth > 0;clog2 = clog2 + 1) depth = depth >> 1; end endfunction但是引入$clog2后,原function可以简化为如下的过程,很显然,通过对系统函数 $clog2的使用,我们大大减少了设计时端口宽度定义时需要code的量。module clog2(a,b); parameter depth = 2034; input [$clog2(depth)-1:0] a; output [$clog2(depth)-1:0]b; //details about the design endmodule 三、额外补充在Xlinix的官网的“44586 - 13.2 Verilog $clog2 function implemented improperly”中,作者发现了13.2版本的Xlinix的ISE对clog2系统函数的错误计算,按照文章中所言:“The $clog2 function returns the ceiling of the logarithm to the base e (natural logarithm) rather than the ceiling of the logarithm to the base 2.”意味着13.2版本的ISE以e为底计算clog2,而非以2为底,官方的回复是ISE 13.2 仅支持Verilog-2001,这个问题在ISE 14.1中进行了修复,所以读者假如使用的开发套件是老版本的,或者不支持Verilog-2005,都有可能因为使用clog2产生问题,需注意。具体额外补充参考如下。44586 - 13.2 Verilog $clog2 function implemented improperly
2023年03月13日
833 阅读
0 评论
1 点赞
HFSS软件笔记
# HFSS软件学习笔记一、HFSS中的边界条件(Boundaries)边界条件定义了求解区域的边界以及不同物体交界处的电磁场特性,是求解麦克斯韦方程的基础。只有在假定场矢量是单值、有界、并且沿空间连续分布的前提下,微分形式的麦克斯韦方程组才是有效的;而在求解区域的边界、不同介质的交界处和场源处,场矢量是不连续的,那么场的导数也就失去了意义。边界条件就是定义跨越不连续边界处的电磁场的特性,因此,正确地理解、定义并设置边界条件,是正确使用HFSS仿真分析电磁场场特性的前提。边界条件的类型:理想导体边界(Perfect E)电场矢量垂直于物体表面,有两种边界被自动设为理想导体边界条件: 1、任何与背景相关联的物体表面将被自动定义为理想导体边界,并命名为outer边界 2、材料设为PEC(理想电导体)的物体表面被自动定义为理想导体边界,并命名为smental理想磁边界/自然边界(Perfect H)电场矢量与物体表面相切,磁场矢量与物体表面垂直。 自然边界(Natural):当理想导体边界和理想磁边界出现交叠时,理想磁边界也称为自然边界 注意:在理想导体边界上叠加理想磁边界将去掉理想导体边界的特性,相当于在理想导体表面开个口,允许电场穿过。有限导体边界(Finite Conductivity)有耗导体/非理性导体边界条件,电场垂直于物体表面,在电磁波的传播方向上电场会愈来愈小 用户需要设置的参数:导电率和导磁率 注意:当物体的材料设置为非理想导体(如铜、铝等金属材料)时,其表面自动定义为有限导体边界条件。辐射边界(Radiation)用于模拟开放的自由空间,模拟波辐射到空间的无限远处的情况,常用于天线问题的分析。当结构中包含辐射边界条件时,HFSS会自动计算结构的远区场。(使用Perfect H边界条件模拟开放空间时,不会计算远区场) 辐射边界条件是自由空间的近似,这种近似的准确程度取决于波的传播方向与辐射边界之间的角度,以及辐射源与边界之间的距离。辐射边界在各个方向上距离辐射体一般不小于1/4个波长。对称边界(Symmetry)模拟理想电壁或理想磁壁对称面,应用对称边界可以构造结构时仅构造一部分,减小结构的尺寸和设计的复杂性,缩短计算时间。 定义对称平面时,需要遵循以下原则: 1、对称平面必须暴露在背景中 2、对称面必须定义在平面表面上,不能定义在曲面上 3、在一个问题上最多只能定义三个正交对称面 决定对称面的类型: 1、如果电场垂直于对称面且对称,使用理想电壁对称面 2、如果磁场垂直于对称面且对称,使用理想磁壁对称面 此外使用对称边界条件需要设置阻抗乘法器: 1、理想电壁对称面将结构分为两部分时,只有一半的电压值和一半的能量被计算,由Zpu=U*U/P计算出的阻抗也只有真实值的一半,所以需要定义2倍的阻抗乘法器。 2、理想磁壁对称面将结构分为两部分时,只有一半的能量被计算,而电压保持不变,由Zpu=U*U/P计算出的阻抗是真实值的2倍,所以需要定义0.5倍的阻抗乘法器。在这里插入图片描述阻抗边界(Impedance)用于模拟已知阻抗的边界表面,如薄膜电阻表面;表面的阻抗Zs=Rs+jXs。 阻抗的计算: number of "Square"=Length(in direction of current flow)/Width Impedance per Square=Desired Lumped Impedance/number of square集总RLC边界(Lumped RLC)类似于阻抗边界条件,利用用户提供的R、L、C值计算出对应的阻抗值 与阻抗边界不同的是,集总RLC边界不需要提供以Ohms/Square为单位的电阻和电抗,而是要给出R、L和C的真实值;之后HFSS就能确定任意频率下集总RLC边界以Ohms/Square为单位的阻抗。分层阻抗边界条件(Layered Impedance)分层阻抗边界条件是用多层结构将物体表面模拟为一个阻抗表面,其效果与阻抗边界条件相同; 与阻抗边界条件不同的是,对于分层阻抗边界条件,HFSS是根据输入的分层结构数据和表面粗糙度来计算表面电阻和表面电抗的。 分层边界条件不支持快速扫频。无限地平面(Infinite Ground Plane)在设置理想导体边界、有限导体边界或阻抗边界时有"Infinite Ground Plane"复选框。 将有限大的边界表面模拟成无限大地平面的作用,设置无限大平面边界后,在后处理中会影响近区、远区辐射场的计算。 定义无限大平面时,需要满足以下条件: 1、必须暴露在背景上 2、必须定义在平面上、 3、无限大平面和对称面的总数不超过3个 4、所有无限大地平面和对称面必须相互垂直主从边界(Master and slave)简称为关联边界条件LBC,主要用于模拟平面周期性结构表面,例如阵列天线。 包括主边界条件(Master)和从边界条件(Slave),总是成对出现,且主边界表面和从边界表面的形状、大小和方向完全相同,主边界表面和从边界表面上的电场存在一定的相位差,该相位差就是周期性结构相邻单元之间存在的相位差。 定义主从边界表面时,用户需要正确设置U、V坐标系,保证主从边界表面大小和方向完全一致。理想匹配层(PML)理想匹配层,是能够完全吸收入射电磁波的假想各项异性材料边界。理想匹配层有两种典型的应用:一是用于外场问题中的自由空间截断,二是用于导波问题中的吸收负载。 对于导波的吸收负载,理想匹配层模拟导波结构均匀地延申到无穷远处。 对于自由空间截断地情况,理想匹配层地作用类似于辐射边界条件,PML表面能够完全吸收入射过来地电磁波。和辐射边界条件相比,理想匹配层因为能够完全吸收入射的电磁波,零反射,因此计算结果更精确;同时理想匹配层表面可以距离辐射体更近(差不过十分之一个波长即可),不需要像辐射边界表面一般需要距离辐射体大约四分之一个波长。二、HFSS中的激励方式(Excitation)HFSS中,激励是一种定义在三维物体表面或者二维物体上的激励源,这种激励源可以是电磁波激励、电压源或者电流源,激励端口是一种允许能量进入或流出几何结构的特殊边界条件类型。激励类型:波端口(Wave Port)默认情况下,所有三维物体和背景之间的接触面都是理想导体边界,没有能量可以进出;波端口设置在背景上,用作模型的激励源并提供一个能量进入/流出的窗口。波端口一般设置在背景平面上,不允许端口平面弯曲。 波端口模式(modes):对于给定横截面的波导或传输线,特定频率下有一系列的解满足相应的边界条件和麦克斯韦方程组,每个解都称之为一种模式,或者说一种波形。通常,模式是根据电场和磁场沿导波系统传输方向上有无分量这一情况来命名的,假设导波系统沿z轴放置,上述分量是指z向的电场分量Ez和磁场分量Hz。 对于Ez=0、Hz=0一类的模,称之为横电磁模,即TEM模; 对于Ez=0、Hz不为0一类模,称之为横电模,即TE模; 对于Ez不为0、Hz=0一类的模,称之为横磁模,即TM模。 端口校准:波端口必须被校准以确保一致的结果;校准的目的有两个,确定场的方向、设置电压的积分路径。 端口平移(Deembed):是指平移端口的位置,查看其对计算结果的影响;选中使用端口平移功能,只影响数据后处理,HFSS不会重新进行仿真计算。HFSS端口平移中正数表示参考平面向模型内部移动,负数则是向外延申。 终端线(Terminal):对于终端驱动的求解类型,终端的S参数反映的是波端口节点电压和电流的线性叠加,通过波端口处的节点电流和电压可以计算出端口的阻抗和S参数矩阵。集总端口(Lumped Por)集总端口激励和波端口激励是HFSS中最常用的两种激励方式。 集总端口激励类似于传统的波端口,与波端口不同的是集总端口可以设置在物体模型内部,且用户需要设定端口阻抗;集总端口直接在端口处计算S参数,设定的端口阻抗即为集总端口上S参数的参考阻抗;另外集总端口不计算端口处的传播常数,因此集总端口无法进行端口平移操作。 集总端口激励的尺寸大小要比波端口小Floquet端口(Floquet Port)与波端口的求解方式类似,Floquet端口求解的反射和传输系数能够以S参数的形式显示。使用Floquet端口激励并结合周期性边界,能够像传统波导端口激励一样轻松的分析周期型结构的电磁特性,从而避免了场求解器复杂的后处理过程。入射波(Incident Wave)是用户设置的朝某一特定方向传播的电磁波,其等相位面与传播方向垂直;入射波照射到器件表面和器件表面的夹角称为入射角。入射波激励常用于雷达反射截面(RCS)问题的计算。 需要设置的参数有:波的传播方向(Poynting Vector)、电场的强度和方向。电压源激励(Voltage)电压源激励定义在两层导体之间的平面上,用理想电压源来表示该平面上的电场激励。 电压源激励时需要设置的参数有:电压的幅度、相位和电场的方向。 注意:电压源激励所在的平面必须远小于工作波长,且平面上的电场是恒定电场;电压源激励是理想的源,没有内阻,因此后处理时不会输出S参数。电流源激励(Current)电流源激励定义于导体表面或者导体表面的缝隙上,用理想电流源来表示该平面上激励。 电流源激励需要设定的参数有:导体表面缝隙的电流幅度、相位和方向。 注意:电流源激励所在的平面/缝隙必须小于工作波长,且平面/缝隙上的电流是恒定的;电流源激励是理想的源,没有内阻,因此后处理时不会输出S参数。磁偏置激励(Magnetic Bias)创建一个铁氧体材料时,必须通过设置磁偏置激励来定义网格的内部偏置场;该偏置场使得铁氧体中的磁性偶极子规则排列,产生一个非零的磁矩。 如果应用的偏置场时均匀的,张量坐标系可以通过旋转全局坐标系来设置 如果应用的偏置场时非均匀的,不允许旋转全局坐标来设置张量坐标系三、求解类型和求解设置1、HFSS中有三种求解类型:模式驱动求解(Driven Model)、终端驱动求解(Driven Terminal)和本征模求解(Eigenmode) 模式驱动求解类型:以模式为基础计算S参数,根据导波内各模式场的入射功率和反射功率来计算S参数矩阵的解。 终端驱动求解类型:以终端为基础计算导体传输线端口的S参数;此时,根据传输线终端的电压和电流来计算S参数矩阵的解。 本征模式求解类型:本征模式求解器主要用于谐振问题的设计与分析,可以用于计算谐振结构的谐振频率和谐振频率处对应的场,也可以用于计算谐振腔体的无载Q值。应用本征模式求解时注意: 不需要设置激励方式 不能定义辐射边界条件 不能进行扫频分析 不能包含铁氧体材料 只有场解结果,没有S参数求解结果2、自适应网格剖分:在分析对象内部搜索误差最大的区域并进行网格的细化,每次网格细化过程中网格增加百分比由用户事先设置,完成一次细化过程后,重新计算并搜索误差最大的区域,然后判断误差是否满足设置的收敛条件。如果满足收敛条件,则完成网格剖分;如果不满足收敛条件,继续下一次网格细化过程,直到满足收敛条件或者达到设置的最大迭代次数为止。3、求解频率(网格自适应剖分频率)的选择HFSS计算时自适应网格剖分是在用户设定的单一频点上进行的,网格剖分完成后,同一个求解设置项下其他频点的求解都是基于前面设定频点上所完成的网格划分。自适应频率设置越高,网格剖分就越细,网格个数就越多,计算结果也相应地更加准确,但同时计算过程中所占用地计算机内存也就越高,计算所花费地时间也越长。下面给出几个常用问题类别的自适应频率的选择:点频或窄带问题:对于点频或者窄带问题,自适应网格剖分直接选择工作频率。宽带问题:对于宽带问题,应该选择最高频率作为自适应网格剖分频率。滤波器问题:对于滤波器问题,由于阻带内电场只存在于断口处,所以自适应频率选择在通带内的高频段。快速扫频问题:对于快速扫频问题,典型的做法就是选择中心频率作为自适应频率。高速数字信号:对于高速数字信号完整性分析问题,需要借助转折频率(Knee Frequency)来决定自适应网格剖分频率4、扫频分析离散扫频(Discrete):是在频带内的指定频点处计算S参数和场解。例如,指定频带范围为1~2GHz、步长为0.25GHz,则会计算在1GHz、1.25GHz、1.5GHz、1.75GHz、2GHz频点处的S参数和场解。默认情况下,使用离散扫频只保存最后计算频率点的场解。如果希望保存指定的所有频率点的场解,需要选中设置对话框中Save Fields复选框。快速扫频(Fast):采用ALPS算法,在很宽的频带范围内搜寻处传输函数的全部零、极点。快速扫频适用于谐振问题和高Q值问题的分析,可以得到场在谐振点附近行为的精确描述。使用快速扫频,一般选择频带中心频率作为自适应网格剖分频率进行网格剖分,计算出该频点的S参数和场分布,然后使用基于ALPS算法的求解器从中心频率处的S参数解和场解来外推整个频带范围的S参数和场解。使用快速扫频,计算时只会求解中心频点处的场解,但在数据后处理时整个扫频范围内的任意频点的场都可以显示。插值扫频(Interpolating):插值扫频使用二分法来计算整个频段内的S参数和场解。使用插值扫频,HFSS自适应选择场解的频率点,并计算相邻两个频点之间的解的误差,找出最大误差,当两点之间的最大误差达到指定的误差收敛标准或者达到了设定的最大频点数目后,扫描完成;其他频率点上的S参数和场解由内插给出。四、HFSS中的变量和Optimetrics模块的使用HFSS不仅能够提供常规的电磁分析,还能够提供优化分析、参数扫描分析、灵敏度分析和统计分析等功能。这些功能都集中在HFSS中的Optimetrics模块中。要使用Optimetrics模块的这些分析和设计功能。首先需要定义和添加相关变量。1、HFSS中变量的定义和使用(1)HFSS中有两种类型的变量:工程变量(Project Variables)和设计变量/本地变量(Local Variables)工程变量和设计变量的区别:工程变量前面有一个"$"前缀,以和本地变量区分工程变量作用区间是整个Project,本地变量作用区间是所在的Design物体模型尺寸、物体的材料属性(工程变量)等都可以使用变量来表示。(2)变量的定义变量名:可以由数字、字母或下划线组成。每个变量在定义时都必须赋一个初始值,变量值可以是数值、数学表达式或者数学函数,也可以是数组、矩阵或者行列式。添加/删除变量:工程变量和设计变量操作不同添加和删除工程变量:Project > Project Variables 或者 [Project Tree] Project > Project Variables 打开 Project Properties 对话框添加和删除设计变量:HFSS > Design Properties 或者 [Project Tree] Design > Design Properties 打开 Design Properties 对话框在设计过程中,也可以直接输入未定义的变量代替设计参数,输入未定义的变量后,HFSS会自动弹出添加变量的对话框2、Optimetrics模块的功能介绍Optimetrics是集成在HFSS中的优化设计模块,该模块通过自动分析设计参数的变化对求解结果的影响,HFSS中Optimetrics模块能够提供如下分析设计功能:参数扫描分析(Parametric)参数扫描分析功能可以用来分析物体的性能随着指定变量的变化而变化的关系,在优化设计之前一般使用参数扫描分析功能来确定被优化变量的合理变化区间参数扫描分析步骤:首先需要定义变量并添加求解设置项HFSS > Optimetrices > Add Parametric...弹出 Setup Sweep Analysis 对话框,添加扫描变量或选中Project Manager 中的 Optimetrics,单击右键 Add > Parametric,弹出 Setup Sweep Analysis 对话框,添加扫描变量设置好扫面变量后,点击”Analyze“就可以进行参数扫描分析查看分析结果优化设计(Optimization)优化设计是HFSS软件结合Optimetrics模块根据特定的优化算法在所有可能的设计变化中寻找出一个满足设计要求的值的过程优化设计的过程:首先需要明确设计要求或设计目标然后用户根据设计要求创建初始结构模型(Nominal Design)、定义设计变量并构造目标函数最后指定优化算法进行优化。在这里插入图片描述调谐分析(Tuning)调谐分析功能是改变变量值的同时实时显示对求解结果的影响程度HFSS中的调谐分析功能是用户在手动改变变量值得同时能实时显示求解结果在这里插入图片描述灵敏度分析(Sensitivity)灵敏度定义为电磁特性/求解结果的变化与电路参数的变化的比值,使用HFSS进行电磁分析时S参数是很常用的一个分析结果。灵敏度分析功能是用来分析设计参数的微小变化对求解结果的影响程度统计分析(Statistical)统计分析功能是利用统计学的观点来研究设计参数容差对求解结果的影响,常用的方法是蒙特卡洛法在这里插入图片描述五、HFSS的数据后处理使用HFSS进行电磁问题的求解分析过程中以及完成求解分析之后,利用数据后处理功能能够直观地给出问题地各种求解信息和求解结果。1、求解信息数据(Solution Data)HFSS > Results > Solution Data 命令,或者右键单机工程树Results节点,从弹出菜单中选择Solution Data命令,可以打开求解信息对话框,显示各种求解信息。2、Results数值结果(1)显示方式HFSS后处理模块能够以多种方式来显示分析数值结果,这些数值结果地显示方式包括:(右击Results > Create Model Solution Data Report)Rectangular Plot:直角坐标图形显示Rectangular Stacked PlotPolar Plot:极坐标图像显示Data Table:数据列表显示Smith Chart: 史密斯圆图显示3D Rectangular Plot:三维直角坐标3D Polar Plot:三维球坐标图形显示Radiation Pattern:辐射方向图(2)参数类型模式驱动求解:Output Variables:用户自定义的输出变量S Parameter:散射参数Y Parameter:导纳参数Z Parameter:阻抗参数VSWR:电压驻波比Gamma:传播常数Port Zo:端口特征阻抗Active S ParameterActive Y ParameterActive Z ParameterActive VSWR终端驱动求解:Output Variables:用户自定义的输出变量S Parameter:散射参数Y Parameter:导纳参数Z Parameter:阻抗参数VSWR:电压驻波比Power:功率Voltage Transform matrix:电压传输矩阵Terminal Port Zo:端口特征阻抗Active S ParameterActive Y ParameterActive Z ParameterActive VSWR(3)输出变量右键单击工程树下的Result节点,从弹出菜单中选择Output Variables命令,便可打开输出变量的定义对话框3、Field Overlays场分布图在HFSS求解完成之后可以通过右击 Field Overlays 来查看电场、磁场、电流密度、坡印廷矢量等场分布图。(1)电场EMag_E:电场幅度瞬时值ComplexMag_E:电场幅度有效值Vector_E:电场矢量(2)磁场HMag_H:磁场幅度瞬时值ComplexMag_H:磁场幅度有效值Vector_H:磁场矢量(3)电流密度JMag_Jvol:体电流密度瞬时值ComplexMag_Jvol:体电流密度有效值Vector_Jvol:体电流密度矢量Mag_Jsurf:面电流密度瞬时值ComplexMag_Jsurf:面电流密度有效值Vector_Jsurf:面电流密度矢量(4)其他Vector_RealPoynting:坡印廷矢量Local\_SAR和Average\_SAR:局部SAR值和平均SAR值六、天线问题的数据后处理1、天线方向图创建天线的方向图:Results > Create Model Solution Data Report > 3D Polar Plot天线的辐射场在固定距离上随球坐标系的角坐标 θ 、φ 分布的图形被称为辐射方向图,简称方向图。方向图通常在远区场确定。用辐射场强表示的方向图称为场强方向图,用辐射功率密度表示的方向图称为功率方向图。2、天线性能参数右击Radiation,创建好查看天线性能参数:右击天线辐射方向图 > Compute Antenna ParametersIncident Power:输入功率HFSS中输入功率是指定义的端口激励功率Acceptable Power:净输入功率净输入功率是指世纪流入天线端口的输入功率,如果分别使用 Pacc 和 Pinc 表示净输入功率和输入功率,对于只有一个传输模式的单端口天线,有: Radiated Power:辐射功率辐射功率是指经由天线辐射到自由空间里的电磁能量,天线的辐射功率可以用坡印廷矢量的曲面积分来计算: Radiation Efficiency:辐射效率辐射效率是辐射功率和净输入功率的比值 Max U:最大辐射强度辐射强度U是指每单位立体角内天线辐射出的功率,Max U是辐射强度的最大值 η 自由空间中的波阻抗为 377Ω,r 为远区场点与天线之间的距离Peak Directivity:方向性系数天线的方向性系数是指在相同的辐射功率和相同的距离的情况下,天线在最大辐射方向上的辐射功率密度与无方向性天线在该方向上的辐射功率密度的比值Peak Gain:天线增益天线增益是指在相同的净输入功率和相同距离的情况下,天线在最大辐射方向上的辐射功率密度与无方向性天线在该方向上的辐射功率密度的比值Peak Realized Gain:最大实际增益天线的最大实际增益是指在相同的输入功率和相同距离的情况下,天线在最大辐射方向上的辐射功率密度与无方向性天线在该方向上的辐射功率密度的比值Front to back Ration:前后向比又称为轴比(Axis Ratio),指方向图中前后瓣的最大比值,代表天线的极化程度3、天线阵的处理由相同的天线单元构成的天线阵的方向图等于单个天线单元的方向图与阵因子的乘积。其中,阵因子取决于天线单元之间的振幅、相位差和相对位置,与天线的类型、尺寸无关。在HFSS中,可以定义天线阵元排列结构和激励方式,然后通过仿真分析分析单个天线单元的方向图等天线参数和阵因子来仿真分析整个天线阵列的方向图等天线参数。HFSS支持两种天线阵列类型:规则排列的均匀天线阵列(Regular Uniform Array)和用户自定义排列(Custom Array)。其中用户自定义阵列:允许用户使用文本文件自定义阵因子信息,然后导入到HFSS软件中,HFSS计算得到阵因子。用户自定义阵列允许更大的灵活性,可以构造天线阵元在空间任意分布的天线阵列。
2023年03月10日
914 阅读
0 评论
2 点赞
1
...
3
4
5
...
25