计算机组成与原理-笔记pre
前言
本人做此笔记一是想记录自己接下来两三年学习的心路历程与心得,二是希望看到这篇笔记的仁人志士能够为我指出一些不妥之处,互相交流,互相点播。
Logisim-pre
元件简述
1、splitter
连着splitter的那一端对应0。
2、符号扩展(Bit Extender)
无论如何连接,输入输出的最低位都是右下角的那个数。如图:
3、奇校验、偶校验(odd parity、even parity)
奇校验:若所传输信号的中第 i 位为1的个数为奇数,则输出对应的第 i 位为1,反之为 0 。
偶校验:若所传输信号的中第 i 位为1的个数为偶数,则输出对应的第 i 位为1,反之为 0 。
下图为奇校验实例图:
4、多路选择器、多路分配器(MUX、DMX)
注意长的一端对应使能端,灰色的点对应的是select data。
MUX与DMX的功能相反,前者是将其中的一个数据选择下来后传输到下一个元件,后者是将一个数据分配到指定的通路中。
5、译码器(Decd)
功能:将n位的二进制码转换为2^n位的独热吗。
6、位选择器(bit selector)
功能:选择输入数据中的一位输出
作用:可以选择输出一个n位数据的每一位。
ps:如果是将信号选择输出为一位信号,可以采用sel,而不用MUX。
7、加法(adder)
注意低位进位和进位。
8、比较器(comparator)
注意将其设置为
9、移位器(shifter)
其实可以看做乘或除2的幂次方。
(左移:输出=输入*2^移动位数)
10、位加(bit adder)
功能:计算输入数据中1的个数
11、触发器(D、T、J-K、S-R、)
同时具有 输入端、输出端、时钟端、使能端、清零端、置1端
12、寄存器(register)
具有 输入端、使能端、输出端、时钟端、清零端,可以设置在时钟的rising edge , falling edge , high level , low level受触发。
13、计数器(counter)
功能:
1、每个时钟周期依次递增1,并具有清零功能
2、通过0/1控制信号和输入端对计数器置数。只有当控制型号为1,且使能端为0时,才能置数成功,接着将控制信号为0,使能端为1,在置完数的基础上继续计数。
14、RAM、ROM
RAM:可读可写,相互分离,将数据接口选择为 Seperatrload and store ports;
ROM:只读;
文件导入:文件头增加一行 v2.0 raw
组合逻辑
1、真值表实现组合逻辑
通过Window->Combinatinal Analysis可以实现输入真值表自动搭建电路,显示卡诺图与逻辑函数表达式,十分方便。
2、题:实现四个4位的数据的排序
解题步骤:
(a)将一位的swap电路拓展为4位的swap电路。
(b)用comparator和4bit swap实现则大数下沉,小数上浮。
(c)最后用上述功能实现冒泡排序。
tips:在最后搭建main的过程中考虑最坏的输入情况,这样搭建起来思路会清晰一点
时序电路
1、有限状态机
- Moore型状态机
特点:一个状态对应一个输出。 - Mealy型状态机
特点:一个状态和一个输入对应一个输出。
如何采用:
其实两者在逻辑方面是可以相互转换的,但是当规定在输入的同时,输出要同时变化时,就要采用Meally型状态机(即在相邻上升沿的开区间时间内输入输出)。
2、Mealy型状态机举例
-
输入:B(1 bit 串行输入,先输入高位)
-
输出:Y0,Y1,Y2,Y3,Y4(Yx的下标x表示2^Bmod5的余数)
-
状态个数:经过列举前几个数,发现一个规律——仅可能有四种余数可能。分别将其成为S0(余1),S1(余2),S2(余4),S3(余3),分别用一个2位的二进制数表示为00,01,10,11.
-
状态转换表:
-
状态转换模块:
根据状态转换表输入即可自动生成。
ps:在设定输入输出的时候,输入设为a1a0,输出设为b1b0,则在用splitter的时候0,1就对应于输入的a0,a1以及输出的b0,b1,不易弄错。
-
输出模块:
-
主函数模块:
3、Moore型状态机举例
- 输入:N;
- 输出:对应N的斐波那契数列Fn
- 状态转换模块:
F(n) = F(n-1) + F (n-2); - Logisim实现如下所示:
笔者发现,若将mux的输出端作为F(n),再和F(n-1)相加后都得到的F(n+1)是不正确的。
注意:经过初始化的那一端,连接的是F(n-1)。
ps:需要注意的几点:
1、当小于的时候停止计算,则counter的max_value一定要大于111,故要将Input位扩展为四位作比较。
2、输入初始化的部分一定一定要注意adder的下面的端口不是从mux之后接出来,而是直接F(n)tunnel,因为在起初,会导致两个1相加,从而快一个时钟周期。
3、mux用到清零端的时候要把Disabled Output设置为zero,否则会输出x。(笔者这里卡了好久555),所以还是最好不要用清零端,或许没啥意义。
Logisim的内容大致就这么多了,接下来是Verilog部分。
Verilog-pre
数据类型以及运算符
assign 语句不能在 always 和initail块中使用。
assign通常与三目运算符配合使用来建立组合逻辑,且只能够对wire型变量赋值。可以用 reg型变量对 wire型变量赋值。
(verilog中无自增自减运算符)
需要注意的几种运算符:
1、逻辑右移运算符 >>算数右移运算符>>>
区别:>>
在最高位补0,>>>
在最高位补符号位。
2、相等比较运算符
==
与!=
的比较结果可能会有不定值 x ,而===
和!==
的结果是确定的0或1。
3、位拼接运算
例如{4{w}}
等价于{w,w,w,w}
,还可多层嵌套。
4、缩减运算符
对一个操作数每一位汇总运算的结果。
模块的引用
modulename uut(.a(x),.b(y),.c(z));
阻塞赋值与非阻塞赋值
- 阻塞赋值是立即赋值,即clk来临时,立即计算RHS并将其赋值给LHS。
- 非阻塞赋值是在clk开始的时候计算RHS,计算好后RHS不变,直到赋值时刻结束时,RHS被赋值。
避免出现冒险与竞争的几点编程要求:
a. 时序电路用非阻塞;
b. always块建立组合逻辑,用阻塞
c. 同一个always块中,有时序逻辑,全用非阻塞
d. 同一个always块中,不要既有非阻塞,又有阻塞
e. 不要在一个以上的always块中为同一个变量赋值
f. 赋值时不用#0来延迟
有符号数的处理
对有符号数的处理就是每个数都加上 $signed(a)
,无论是在比大小,还是在做运算,每个数都要套上signed,否则verilog自动将符号数转化为无符号数,就会功亏一篑。
宏定义
注意在引用的时候也要加上`。
组合逻辑
- assign
- always @(*)+ case语句 + 阻塞赋值
HdlBits Verilog笔记
下面的部分是FSM之前的,FSM我还没做,做好了再更~
1、Gates(门)
- NOR gate: assign out = ~(a | b);
- NOR-或非
- XOR-异或 (^)
- XNOR-异或非
2、default_nettype none
放在module开头可以转换缺省类型
3、Bitwise and logical(位运算和逻辑运算)
按位或: a | b return a number with 4bits
逻辑或: a || b return 1 or 0
same as &.&&.
4、Concatenations(位拼接)
If we’re certain about the width of each signal, using the concatenation operator is equivalent and shorter: assign {w,x,y,z} = {a,b,b,c};
unsized constants are not allowed in concatenations!!
注意 {}
字符串中的24{in[7]}
外面还有一个大括号{}
!!
assign out = {{24{in[7]}},in[7:0]};
assign {o2, o1, o0} = vec;//表示分别输出vec的第2位、第1位、第0位
5、Module -instantiate(模块实例)
- By position
mod_a instance1 ( wa, wb, wc );
One drawback of this syntax is that the module’s port list changes , all instantiations of the module will also need to be found and changes to match the new module.
- By name
mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );
调用module的时候,要对临时调用的module命名,例如上述的 instance1和 instance2。
6、Full adder(全加器)
module add1 ( input a, input b, input cin, output sum, output cout );
// Full adder module here
assign sum = a^b^cin;
assign cout = (a&b)|(a&cin)|(b&cin);
endmodule
另一种写法:(这种写法的好处在于无论输入输出是几位的都成立)
module top_module(
input a, b, cin,
output cout, sum );
assign {cout,sum}=a+b+cin;
endmodule
7、Signed two 32bits numbers adder(有符号的两数相加)
module top_module(
input [31:0] a,
input [31:0] b,
input sub,
output [31:0] sum
);
wire cout1;
wire cout2;
wire [31:0] b_final;
always @(*)
begin
case(sub)
1'b0:b_final = b ;
1'b1:b_final = ~b + 1;
endcase
end
add16 uut1 (a[15:0],b_final[15:0],0,sum[15:0],cout1);
add16 uut2 (a[31:16],b_final[31:16],cout1,sum[31:16],cout2);
endmodule
8、Avoid to make latches(防止产生锁存器)
- case-endcase最好要有 default
- if语句最好要有else语句
- casez
// synthesis verilog_input_version verilog_2001
module top_module (
input [7:0] in,
output reg [2:0] pos );
always@(*)
begin
casez(in)
8'b???????1:pos = 3'b000;
8'b??????10:pos = 3'b001;
8'b?????100:pos = 3'b010;
8'b????1000:pos = 3'b011;
8'b???10000:pos = 3'b100;
8'b??100000:pos = 3'b101;
8'b?1000000:pos = 3'b110;
8'b10000000:pos = 3'b111;
default:pos = 3'b000;
endcase
end
endmodule
!!!防治锁存的最好的方法是再case语句之前将其赋初值!!!见下例
// synthesis verilog_input_version verilog_2001
module top_module (
input [15:0] scancode,
output reg left,
output reg down,
output reg right,
output reg up );
always@(*)
begin
left = 0;down = 0;right = 0;up = 0;
case(scancode)
16'he06b:left = 1;
16'he072:down = 1;
16'he074:right = 1;
16'he075:up = 1;
endcase
end
endmodule
9、Reduction(简化)
module top_module (
input [7:0] in,
output parity);
assign parity = ^ in[7:0];//表示对in的八位同时取xor
endmodule
module top_module(
input [99:0] in,
output out_and,
output out_or,
output out_xor
);
assign out_and = & in[99:0];
assign out_or = | in[99:0];
assign out_xor = ^ in[99:0];
endmodule
10、MUX(多路选择器)
- 同样防止锁存
module top_module(
input [15:0] a, b, c, d, e, f, g, h, i,
input [3:0] sel,
output reg [15:0] out );
always @(*)begin
out=16'hffff;//防止锁存,无需用到default
case(sel)
4'd0:out = a;
4'd1:out = b;
4'd2:out = c;
4'd3:out = d;
4'd4:out = e;
4'd5:out = f;
4'd6:out = g;
4'd7:out = h;
4'd8:out = i;
endcase
end
endmodule
- 设置一个256位的左路选择器(此方法很妙)叫做 向量索引
module top_module (
input [255:0] in,
input [7:0] sel,
output out
);
assign out = in[sel];
endmodule
- 再看个1024位的
module top_module(
input [1023:0] in,
input [7:0] sel,
output [3:0] out );
assign out = {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[sel*4+0]};
endmodule
11、Overflow(溢出)
module top_module (
input [7:0] a,
input [7:0] b,
output [7:0] s,
output overflow
); //
// assign s = ...
// assign overflow = ...
assign s = a + b;
assign overflow = (a[7]&b[7]&~s[7])|(~a[7]&~b[7]&s[7]);
endmodule
溢出与否只需要比较一个两个数的最高位和加完之后的数的最高位。
12、Asynchronous reset(异步复位)
module top_module (
input clk,
input areset, // active high asynchronous reset
input [7:0] d,
output [7:0] q
);
always@(posedge clk or posedge areset)
begin
if(areset)
q <= 0;
else
q <= d;
end
endmodule
13、Decimal adder(十进制加法器)
转自https://blog.youkuaiyun.com/haojie_duan/article/details/113469349
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:1] ena,
output [15:0] q);
reg [3:0] ones;
reg [3:0] tens;
reg [3:0] hundreds;
reg [3:0] thousands;
always@(posedge clk)begin
if(reset)begin
ones <= 4'd0;
end
else if(ones == 4'd9)begin
ones <= 4'd0;
end
else begin
ones <= ones + 1'b1;
end
end
always@(posedge clk)begin
if(reset)begin
tens <= 4'd0;
end
else if(tens == 4'd9 && ones == 4'd9)begin
tens <= 4'd0;
end
else if(ones == 4'd9) begin
tens <= tens + 1'b1;
end
end
always@(posedge clk)begin
if(reset)begin
hundreds <= 4'd0;
end
else if(hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9)begin
hundreds <= 4'd0;
end
else if(tens == 4'd9 && ones == 4'd9) begin
hundreds <= hundreds + 1'b1;
end
end
always@(posedge clk)begin
if(reset)begin
thousands <= 4'd0;
end
else if(thousands == 4'd9 && hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9)begin
thousands <= 4'd0;
end
else if(hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9) begin
thousands <= thousands + 1'b1;
end
end
assign q = {thousands, hundreds, tens, ones};
assign ena[1] = (ones == 4'd9) ? 1'b1 : 1'b0;
assign ena[2] = (tens == 4'd9 && ones == 4'd9) ? 1'b1 : 1'b0;
assign ena[3] = (hundreds == 4'd9 && tens == 4'd9 && ones == 4'd9) ? 1'b1 : 1'b0;
endmodule
14、 Arthmetic Shift(算数移位器)
module top_module(
input clk,
input load,
input ena,
input [1:0] amount,
input [63:0] data,
output reg [63:0] q);
always@(posedge clk)begin
if(load)begin
q <= data;
end
else if(ena)begin
case(amount)
2'b00:q <= {q[62:0],1'd0};
2'b01:q <= {q[55:0],8'd0};
2'b10:q <= {q[63],q[63:1]};
2'b11:q <= {{8{q[63]}},q[63:8]};
endcase
end
end
endmodule
总结
先写这么多,verilog的FSM部分和mips部分有空再更。写得较为草率,若有错误,请求指出 ~