第一章 概念辨析
组合逻辑&时序逻辑
- 波形图中,表达时序逻辑时如果时钟和数据是对齐的,则默认当前时钟沿采集到的数据位在该时钟上升沿前一时刻的值。表达组合逻辑时如果时钟和数据是对齐的,则默认当前时钟沿采集到的数据为该始终上升沿同一时刻的值。
- 组合逻辑和时序逻辑的区别:
主要是看数据工作是不是在时钟沿下进行的。在 FPGA 的设计中,复杂的电路设计都要用到时序逻辑电路,往往都是以时序逻辑电路为主,组合逻辑为辅的混合逻辑电路。
- 组合逻辑会存在险竞争冒险,会引起电路的不稳定性和工作时的不确定性
- 时序逻辑最基本的单元就是寄存器,寄存器一般由D触发器构成
- 时序电路“延一拍”,key_in为高,led_out延迟一拍才变高
Verilog的三种描述方式(行为级、RTL级、门级)
行为级
结构化过程语句:initial和always,通常借助一些(+)(-)等运算符;
左值必须是reg类型,右值无要求;
过程赋值语句。
module Full_Add_4b_2( A, B, Cin, Sum, Cout );
input[3:0] A;
input[3:0] B;
input Cin;
output[3:0] Sum;
output Cout;
reg [3:0] Sum;
reg Cout;
always @(A or B or Cin) begin
{Cout, Sum} <= A + B + Cin;
end
endmodule
RTL级
基本语句为连续赋值语句:assign;
左值必须是net类型,右值无要求
使用寄存器这一级别的描述方式来描述电路的数据流方式。RTL在很大程度上是对流水线原理图的描述。接近实际电路结构的描述,可以精确描述电路的原理、执行顺序等。其目的在于可综合。
module Full_Add_4b_1( A, B, Cin, Sum, Cout );
input[3:0] A;
input[3:0] B;
input Cin;
output[3:0] Sum;
output Cout;
assign {Cout, Sum} = A + B + Cin;
endmodule
门级
使用逻辑门这一级别来描述,and、or……,
输出部分必须是net类型,门级原语本质是模块实例调用,符合端口连接规则。
RTL 中的寄存器和组合逻辑,直接反应了逻辑门直接的关系,更加接近底层,接近硬件,一般EDA工具可以把RTL描述自动编译为门级描述。所以一般不直接使用门级编程。
module Full_Add_1b_3( A, B, Cin, Sum, Cout );
input A;
input B;
input Cin;
output Sum;
output Cout;
wire S1, T1, T2, T3; // -- statements -- //
xor x1 (S1, A, B);
xor x2 (Sum, S1, Cin);
and A1 (T3, A, B );
and A2 (T2, B, Cin);
and A3 (T1, A, Cin);
or O1 (Cout, T1, T2, T3 );
endmodule
//两位全加器
`include "Full_Add_1b_3.v"
module Full_Add_2b_3( FA, FB, FCin, FSum, FCout ) ;
parameter SIZE = 2;
input [SIZE:1] FA;
input [SIZE:1] FB;
input FCin;
output [SIZE:1] FSum;
output FCout;
wire FTemp;
Full_Add_1b_3 FA1( .A (FA[1]), .B (FB[1]), .Cin (FCin) , .Sum (FSum[1]), .Cout (FTemp));
Full_Add_1b_3 FA2( .A (FA[2]), .B (FB[2]), .Cin (FTemp) , .Sum (FSum[2]), .Cout (FCout));
endmodule
xilinx vivado的五种仿真模式和区别
摘自https://www.elecfans.com/pld/703509.html
vivado的仿真暂分为五种仿真模式,分别为:
- run behavioral simulation-----行为级仿真,行为级别的仿真通常也说功能仿真。
- post-synthesis function simulation-----综合后的功能仿真。
- post-synthesis timing simulation-----综合后带时序信息的仿真,综合后带时序信息的仿真比较接近于真实的时序。
- post-implementation function simulation-----布线后的功能仿真。
- post-implementation timing simulation-----(布局布线后的仿真) 执行后的时序仿真,该仿真时最接近真实的时序波形。
数字电路设计中一般包括3个大的阶段:源代码输入、综合和实现,而电路仿真的切入点也基本与这些阶段相吻合,根据适用的设计阶段的不同仿真可以分为RTL行为级仿真、综合后门级功能仿真和时序仿真。这种仿真轮廓的模型不仅适FPGA/CPLD设计,同样适合IC设计。
行为级仿真时必须的,能够确保你所设计功能是正确的,综合后时序仿真是有必要的,能够排除大部分的时序问题,至于后仿真,只能是解决疑难杂症时再采取的大招,非常费时间,一般不建议做后仿真。
RTL行为级仿真
在大部分设计中执行的第一个仿真将是RTL行为级仿真。这个阶段的仿真可以用来检查代码中的语法错误以及代码行为的正确性,其中不包括延时信息。如果没有实例化一些与器件相关的特殊底层元件的话,这个阶段的仿真也可以做到与器件无关。因此在设计的初期阶段不使用特殊底层元件即可以提高代码的可读性、可维护性,又可以提高仿真效率,且容易被重用。(绝大部分设计人员将这个阶段的仿真叫功能仿真!)
综合后门级功能仿真 (前仿真)
一般在设计流程中的第二个仿真是综合后门级功能仿真。绝大多数的综合工具除了可以输出一个标准网表文件以外,还可以输出Verilog或者VHDL网表,其中标准网表文件是用来在各个工具之间传递设计数据的,并不能用来做仿真使用,而输出的Verilog或者VHDL网表可以用来仿真,之所以叫门级仿真是因为综合工具给出的仿真网表已经是与生产厂家的器件的底层元件模型对应起来了,所以为了进行综合后仿真必须在仿真过程中加入厂家的器件库,对仿真器进行一些必要的配置,不然仿真器并不认识其中的底层元件,无法进行仿真。Xilinx公司的集成开发环境ISE中并不支持综合后仿真,而是使用映射前门级仿真代替,对于Xilinx开发环境来说,这两个仿真之间差异很小。
时序仿真 (后仿真)
在设计流程中的最后一个仿真是时序仿真。在设计布局布线完成以后可以提供一个时序仿真模型,这种模型中也包括了器件的一些信息,同时还会提供一个SDF时序标注文件(Standard Delay format Timing Anotation)。SDF时序标注最初使用在Verilog语言的设计中,现在VHDL语言的设计中也引用了这个概念。对于一般的设计者来说并不需知道SDF。
D触发器(D Flip Flop,DFF)
- D触发器的功能:
1、在一个脉冲信号上升沿或下降沿的作用下,将信号从输入端D送到输出端Q,如果时钟脉冲的边沿信号未出现,即使输入信号改变,输出信号仍然保持原值。
2、寄存器拥有复位清零功能,复位分为同步复位和异步复位
3、能够存储一位二进制码
- D触发器分为两种,一种是同步复位D触发器,一种是异步触发D触发器。
同步复位D触发器
- 同步复位D触发器,复位与时钟同步。即时钟上升沿到来时检测到按键的复位操作才有效
异步复位D触发器
- 异步复位触发器,复位与时钟不同步。寄存器不关心时钟上升沿来不来,只要有按键按下,就立即执行复位操作。但是复位释放时仍需要等到时钟上升沿才能检测到key_in的值 并给led_out
第二章 向量
门和向量
在[3:0] 中,您将获得一个四位输入向量。我们想知道每个位与其邻居之间的一些关系:
-
out_both:本输出向量的每一位应该指示是否两个相应的输入位和它的邻居到左(较高的指数)是“1”。例如,out_both[2]应该表明in[2]和in[3]是否都为 1。由于in[3]左边没有邻居,所以答案很明显,所以我们不需要知道out_both[3]。
-
out_any:此输出向量的每一位都应指示是否有任何相应的输入位及其右侧的邻居为“1”。例如,out_any[2]应该表明in[2]或in[1]是否为 1。由于in[0]右边没有邻居,答案很明显,所以我们不需要知道out_any[0]。
-
out_different:该输出向量的每一位应指示相应的输入位是否与其左侧的相邻位不同。例如,out_different[2]应该指示in[2]是否与in[3] 不同。对于这部分,将向量视为环绕,因此in[3]左侧的邻居是in[0]。
-
代码实现:
module top_module(
input [3:0] in,
output [2:0] out_both,
output [3:1] out_any,
output [3:0] out_different );
assign out_both = in[2:0] & in[3:1];
assign out_any = in[2:0] | in[3:1];
assign out_different = in[3:0] ^ {in[0],in[3:1]};
endmodule
- 验证结果
向量/向量100
向量0(Vector0)
矢量用于使用一个名称对相关信号进行分组,以便更方便地操作。例如,wire [7:0] w; 声明了一个名为w的 8 位向量,它在功能上等同于具有 8 条单独的线。
请注意,向量的声明将维度放在向量名称之前,这与 C 语法相比是不寻常的。然而,部分选择了尺寸后,如你所期望的矢量名称。
wire [99:0] my_vector; // Declare a 100-element vector
assign out = my_vector[10]; // Part-select one bit out of the vector
构建一个具有一个 3 位输入的电路,然后输出相同的向量,并将其拆分为三个单独的 1 位输出。将输出连接o0到输入向量的位置 0、o1位置 1 等。
在图中,旁边带有数字的刻度线表示向量(或“总线”)的宽度,而不是为向量中的每一位绘制单独的线。
//推荐解法一:
//assign是可以出现在for循环(begin…end)中的,
//但是需要generate来包住!
module top_module(
input [7:0] in,
output [7:0] out
);
genvar i;
generate
for(i=0;i<8;i=i+1)begin:reverse_bits
assign out[7-i] = in[i];
end
endgenerate
endmodule
//解法二:
module top_module(
input [7:0] in,
output [7:0] out
);
integer i;
always@(*)begin
for(i=0;i<8;i=i+1)begin
out[7-i] = in[i];
end
end
endmodule
- 验证结果
向量100
组合for循环:向量反转2
给定一个 100 位的输入向量 [99:0],反转其位顺序。
代码实现:
module top_module( //解法一
input [99:0] in,
output [99:0] out
);
integer i;
always@(*) begin
for( i = 0 ;i <= 99;i = i + 1 ) begin
out[i] = in[99-i];
end
end
endmodule
module top_module( //推荐解法二
input [99:0] in,
output [99:0] out
);
generate
genvar i;
for(i = 0; i <= 99; i = i + 1)begin:reverse
assign out[i] = in[99 - i];
end
endgenerate
endmodule
Always if
- Build a 2-to-1 mux that chooses between a and b. Choose b if both sel_b1 and sel_b2 are true. Otherwise, choose a. Do the same twice, once using assign statements and once using a procedural if statement.
// synthesis verilog_input_version verilog_2001
module top_module(
input a,
input b,
input sel_b1,
input sel_b2,
output wire out_assign,
output reg out_always );
assign out_assign = (sel_b1&sel_b2)?b:a;//(sel_b1 && sel_b1)? b: a;
always @(*) begin
if (sel_b1==1 && sel_b2==1) begin //(sel_b1==1'b1 && sel_b1==1'b1) begin
out_always <= b;
end else begin
out_always <= a;
end
end
endmodule
第三章 组合逻辑
优先级编码器
- 优先级编码器是一个组合电路,给定一个输入位向量时,输出第一的位置1中的向量位。例如,给定输入8’b100 1 0000的 8 位优先级编码器将输出3’d4,因为 bit[4] 是第一个高位。
构建一个 4 位优先级编码器。对于这个问题,如果没有一个输入位为高(即输入为零),则输出零。请注意,一个 4 位数字有 16 种可能的组合。
module top_module (
input [3:0] in,
output reg [1:0] pos );
always@(*) begin
case(in[3:0])
4'b0000: pos = 2'b00;
4'b0001: pos = 2'b00;
4'b0010: pos = 2'b01;
4'b0011: pos = 2'b00;
4'b0100: pos = 2'b10;
4'b0101: pos = 2'b00;
4'b0110: pos = 2'b01;
4'b0111: pos = 2'b00;
4'b1000: pos = 2'b11;
4'b1001: pos = 2'b00;
4'b1010: pos = 2'b01;
4'b1011: pos = 2'b00;
4'b1100: pos = 2'b10;
4'b1101: pos = 2'b00;
4'b1110: pos = 2'b01;
4'b1111: pos = 2'b00;
default: pos = 2'b00;
endcase
end
endmodule
casez的优先级编码器
构建一个8 位输入优先编码器。给定一个 8 位向量,输出应报告向量中的第一个位1。如果输入向量没有高位,则报告零。例如,输入8’b100 1 0000应该输出3’d4,因为 bit[4] 是第一个高位。
从上一个练习中,case 语句中有 256 个 case。如果 case 语句中的 case 项支持 don’t-care 位,我们可以减少这种情况(减少到 9 个 case)。这就是z 的情况:它在比较中将具有z值的位视为不关心的。
例如:这将实现上一个练习中的 4 输入优先级编码器:
always @(*) begin
casez (in[3:0])
4'bzzz1: out = 0; // in[3:1] can be anything
4'bzz1z: out = 1;
4'bz1zz: out = 2;
4'b1zzz: out = 3;
default: out = 0;
endcase
end
case 语句的行为就好像每个项目都是按顺序检查的(实际上,它的作用更像是生成一个巨大的真值表后制作门)。请注意某些输入(例如4’b1111)如何匹配多个 case 项。选择第一个匹配项(因此4’b1111匹配第一项out = 0,但不匹配后面的任何项)。
还有一个类似的casex将x和z 都视为无关紧要。在casez上使用casex没有多大意义。
?是z的同义词。所以2’bz0和2’b?0是一样的。
- 代码实现:
module top_module (
input [7:0] in,
output reg [2:0] pos );
always @(*) begin
casez (in[7:0])
8'bzzzzzzz1: pos = 3'b000;
8'bzzzzzz1z: pos = 3'b001;
8'bzzzzz1zz: pos = 3'b010;
8'bzzzz1zzz: pos = 3'b011;
8'bzzz1zzzz: pos = 3'b100;
8'bzz1zzzzz: pos = 3'b101;
8'bz1zzzzzz: pos = 3'b110;
8'b1zzzzzzz: pos = 3'b111;
default: pos = 3'b000;
endcase
end
endmodule
- 验证结果
避免锁存
假设您正在构建一个电路来处理来自游戏的 PS/2 键盘的扫描码。给定接收到的最后两个字节的扫描码,您需要指出是否按下了键盘上的箭头键之一。这涉及一个相当简单的映射,它可以实现为具有四个 case 的 case 语句(或 if-elseif)。
您的电路有一个 16 位输入和四个输出。构建此电路以识别这四个扫描码并断言正确的输出。
为避免创建锁存器,必须在所有可能的条件下为所有输出分配一个值。仅仅有一个默认情况是不够的。您必须在所有四种情况和默认情况下为所有四个输出分配一个值。这可能涉及许多不必要的打字。解决此问题的一种简单方法是在 case 语句之前为输出分配一个“默认值” :
always @(*) begin
up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
case (scancode)
... // Set to 1 as necessary.
endcase
end
这种代码风格确保在所有可能的情况下为输出分配一个值(0),除非 case 语句覆盖分配。这也意味着default: case 项变得不必要了。
提醒:逻辑合成器生成一个组合电路,其行为与代码描述的相同。硬件不会按顺序“执行”代码行。
- 代码实现:
module top_module (
input [15:0] scancode,
output reg left,
output reg down,
output reg right,
output reg up );
always @(*) begin
up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
case (scancode)
16'he06b: left = 1'b1;
16'he072: down = 1'b1;
16'he074: right = 1'b1;
16'he075: up = 1'b1;
endcase
end
endmodule
- 验证结果:
三元条件运算符
- verilog 有一个三元条件运算符 ( ? : ) ,像 C语言一样。
(condition ? if_true : if_false)
这可用于根据一行上的条件(多路复用器!)选择两个值之一,而无需在组合 always 块中使用 if-then。
例如:
(0 ? 3 : 5) // This is 5 because the condition is false.
(sel ? b : a) // A 2-to-1 multiplexer between a and b selected by sel.
always @(posedge clk) // A T-flip-flop.
q <= toggle ? ~q : q;
always @(*) // State transition logic for a one-input FSM
case (state)
A: next = w ? B : A;
B: next = w ? A : B;
endcase
assign out = ena ? q : 1'bz; // A tri-state buffer
((sel[1:0] == 2'h0) ? a : // A 3-to-1 mux
(sel[1:0] == 2'h1) ? b :
c )
-
练习:
给定四个无符号数,找出最小值。无符号数可以与标准比较运算符 (a < b) 进行比较。使用条件运算符创建两路最小电路,然后组合其中的一些来创建 4 路最小电路。您可能需要一些用于中间结果的向量。 -
代码实现:
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);
wire [7:0]min1,min2;
assign min1 = (a > b)? b : a;
assign min2 = (c > d)? d : c;
assign min = (min1 > min2)? min2 : min1;
endmodule
- 验证结果:
组合电路A和B
- 顶层设计包含两个实例,每个子电路 A 和 B,如下所示。
module top_module (input x, input y, output z);
wire zA1,zB1,zA2,zB2;
A u_A1(x,y,zA1);
B u_B1(x,y,zB1);
A u_A2(x,y,zA2);
B u_B2(x,y,zB2);
assign z = (zA1|zB1)^(zA2&zB2);
endmodule
module A(input x, input y, output z);
assign z = (x^y)&x;
endmodule
module B(input x, input y, output z);
assign z = ~(x^y);
endmodule
- (不推荐在module中使用task)第二种是使用task,从下面代码中可以看出来,同一个module的不同task的参数名是可以一样的,不会冲突,我理解可以认为就是局部变量;另外需要注意的就是task的调用必须在过程块中,比如always。
module top_module (input x, input y, output z);
wire wire1,wire2,wire3,wire4;
always@(*)begin
A(x,y,wire1);
A(x,y,wire2);
B(x,y,wire3);
B(x,y,wire4);
end
assign z = (wire1|wire2)^(wire3&wire4);
//--------------task A-------------------
task A;
input x,y;
output z;
z = (x ^ y) & x;
endtask
//------------------task B-----------------
task B;
input x,y;
output z;
z = (x == y)?1:0;
endtask
endmodule
多路选择器
- 常用实现方式
1、三目运算符 (cond ? iftrue : iffalse),适用sel数量≤5
2、case语句,适用于sel数量≤16
3、矢量索引可以是可变的,只要合成器能识别出所选比特的宽度是恒定的。特别是,使用可变索引从一个矢量中选择一位也是可行的。out = in[sel]、out = {in[4sel+3], in[4sel+2], in [4sel+1] , in[4sel]};
256对1多路选择器
创建一个 1 位宽、256 对1 的多路选择器。256 个输入全部打包成一个 256 位的输入向量。sel=0 应该选择in[0], sel=1 选择位in[1], sel=2 选择位in[2]等。
- 代码实现:
module top_module(
input [255:0] in,
input [7:0] sel,
output out );
assign out = in[sel];
endmodule
256对1的4位多路选择器
创建一个 4 位宽、256 比 1 的多路复用器。256 个 4 位输入全部打包成一个 1024 位输入向量。sel=0 应该选择[3:0] 中的位, sel=1 选择[7:4] 中的位, sel=2 选择[11:8] 中的位,依此类推。
- 代码实现:
module top_module(
input [1023:0] in,
input [7:0] sel,
output [3:0] out );
assign out = {in[4*sel+3],in[4*sel+2],in[4*sel+1],in[4*sel]};
endmodule
第五章 建模
组合逻辑
响铃还是振动?
- 假设您正在设计一个电路来控制手机的响铃器和振动电机。每当电话因来电需要响铃 (input ring)时,您的电路必须打开响铃器 (output ringer = 1) 或电机 (output motor = 1),但不能同时打开两者。如果手机处于振动模式 (input vibrate_mode = 1),请打开电机。否则,打开振铃器。
尝试仅使用assign语句,看看是否可以将问题描述转换为逻辑门的集合。
设计提示:在设计电路时,人们通常必须“向后”思考问题,从输出开始,然后向输入反向工作。这通常与人们对(顺序的、命令式的)编程问题的看法相反,在这种问题中,人们会首先查看输入,然后决定一个动作(或输出)。对于顺序程序,人们经常会想“如果(输入是 ___ )那么(输出应该是 ___ )”。另一方面,硬件设计人员通常认为“当(输入为 ___ )时(输出应为 ___ )”。
上面的问题描述是以适合软件编程的命令式形式编写的(if ring then do this),因此您必须将其转换为更适合硬件实现的声明式形式(assign ringer = ___)。能够思考并在两种风格之间进行转换是硬件设计所需的最重要技能之一。
module top_module (
input ring,
input vibrate_mode,
output ringer, // Make sound
output motor // Vibrate
);
assign ringer = (~vibrate_mode) & ring;
assign motor = vibrate_mode & ring;
endmodule
- 验证结果
恒温器
加热/冷却恒温器同时控制加热器(冬季)和空调(夏季)。实施一个电路,根据需要打开和关闭加热器、空调和风扇。
恒温器可以处于两种模式之一:加热 ( mode = 1) 和冷却 ( mode = 0)。在制热模式下,天气太冷时打开加热器(too_cold = 1),但不要使用空调。在制冷模式下,当温度过高时 ( too_hot = 1)打开空调,但不要打开加热器。当加热器或空调打开时,还要打开风扇使空气流通。此外,即使加热器和空调关闭,用户还可以要求风扇开启(fan_on = 1)。
尝试仅使用assign语句,看看是否可以将问题描述转换为逻辑门的集合。
- 问题分析:
写出真值表–(卡诺图化简)→写出逻辑表达式
too_hot | too_cold | mode | fan_on | hot | aircon | fan | |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | x | 0 | 0 | 0 | |
1 | 0 | 0 | x | 0 | 1 | 1 | |
0 | 0 | 1 | x | 0 | 0 | 0 | |
0 | 1 | 1 | x | 1 | 0 | 1 | |
0 | 0 | 0 | 1 | 0 | 0 | 1 |
直接写出逻辑表达式:
heater = too_cold & mode;
aircon = too_hot & (~mode);
fan = heater | aircon | fan_on;
- 代码实现:
module top_module (
input too_cold,
input too_hot,
input mode,
input fan_on,
output heater,
output aircon,
output fan
);
assign heater = too_cold & mode;
assign aircon = too_hot & (~mode);
assign fan = heater | aircon | fan_on;
endmodule
- 验证结果
卡诺图
四变量2
- 实现下面卡诺图描述的电路。注:d可以根据化简需求自己制定为0或是1。
- 代码实现
module top_module(
input a,
input b,
input c,
input d,
output out );
assign out = a | (~b&c);
endmodule
- 验证结果
卡诺图3
实现下面卡诺图描述的电路。
- 代码实现:
module top_module(
input a,
input b,
input c,
input d,
output out );
assign out = ((~a)&b&(~c)&(~d)) | (a&(~b)&(~c)&(~d)) | ((~a)&(~b)&(~c)&d) | (a&b&(~c)&d) |
(a&(~b)&c&d) | ((~a)&b&c&d) | ((~a)&(~b)&c&(~d)) | (a&b&c&(~d));
endmodule
- 验证结果:
逻辑函数的规范范式:SOP与POS形式
- SOP(Sum of Product) 和 POS(Product of Sum) 是表示缩减逻辑表达式的方法。
- 两者的基本区别是,SOP将布尔函数表达为乘积(逻辑和)项的和(逻辑OR),而POS将逻辑函数表达为和(逻辑OR)项的乘积(逻辑AND)
如下进行SOP形式的逻辑函数表示:
由此 SOP形式的逻辑函数:
F = (~A&B) | (A&B )
如下进行POS形式的逻辑函数表示:
由此 POS形式的逻辑函数:
F = (A + B)& (~A + B)
- 从真值表到标准式
SOP标准式:找出真值表中所有输出为1的表项,按照输入的情况,为1用变量表示,为0则用反变量表示,得出若干乘积项,然后求和。
POS标准式:找出真值表中所有输出为0的表项,按照输入的情况,为1用反变量表示,为0则用原变量表示,得出若干求和项,然后求积。
若针对所有F=1的表项,可轻松写出SOP标准式如下:
F = (~A & B & C) | (A & ~B & ~C) | (A & B & ~C) | (A & B & C)
若针对所有F=0的表项,可轻松写出POS标准式如下:
F = (A | B | C) & (A | B | ~C) & (A | ~B | C) & (A | ~B | C)
- 从卡诺图到最简SOP式(从标准SOP式到最简SOP式)
标准表达式并非最简表达式,从标准SOP式到最简SOP式为一个标准的逻辑化简的过程。此时可以引入卡诺图,来寻找最小项的合并规律,从而可以轻易的进行化简工作
1、求sop最简式,卡诺图:纵向为cd,横向为ab
2、求pos最简式,卡诺图:纵向为c+d,横向为a+b
- 练习:实现一个有四输入(a.b,c,d)的单输出数字系统,当2、7或15出现在输入端时,生成逻辑1,当0、1、4、5、6、9、10 13或14出现时,生成逻辑0。数字3、8、11和12的输入不会出现在这个系统中。例如,7对应于a和b。c,d分别被设为0,1,1,1。 确定最小SOP格式的输出out_sop和最小POS格式的输出out_pos。
- 代码实现:
module top_module (
input a,
input b,
input c,
input d,
output out_sop,
output out_pos
);
assign out_sop = (~a & ~b & c) | (c & d);
assign out_pos = c & (~a | b) & (~b | d);
endmodule
用多路选择器实现卡诺图
- 对于下面的卡诺图,用一个4-1多路选择器和不限的2-1多路选择器,但2-1多路选择器的使用要尽可能少。你不允许使用任何其他逻辑门,你必须使用a和b作为多路复用器选择器的输入,如下面的4- 1多路选择器所示。
您只实现了标记为top_module的部分,以便整个电路(包括 4 对 1 多路选择器)实现卡诺图。
- 代码实现:
//按照c,d的排列,对照卡诺图,对应行的a,b所有的值,即为输出mux_in。
module top_module (
input c,
input d,
output [3:0] mux_in
);
always@(*)begin
case({c,d})
2'b00: mux_in = 4'b0100;
2'b01: mux_in = 4'b0001;
2'b10: mux_in = 4'b0101;
2'b11: mux_in = 4'b1001;
endcase
end
endmodule
加粗样式