Verilog处理器设计


第一部分
第二部分
第三部分
第四部分
第五部分

我们在Verilog中设计Little Man Computer

关于LMC 的文章在Habré上。

这台计算机的在线模拟器在这里

我们编写一个包含四个(N = 2)个四位(M = 4)字的RAM / RAM模块。 单击按钮时,数据将从adr的data_in加载到RAM中
module R0 #(parameter N = 2, M = 4) ( input RAM_button, // input [N-1:0] adr, // input [M-1:0] data_in, //   output [M-1:0] RAM_out //   ); reg [M-1:0] mem [2**N-1:0]; //  mem always @(posedge RAM_button) //    mem [adr] <= data_in; //     data_in assign RAM_out = mem[adr]; // RAM_out    endmodule 

作为外部发生器,连接555 CMOS 定时器 (工作电压为3.3V)。
我们将555计时器连接到计数器,将计数器连接到RAM的地址输入:
 module R1 #(parameter N = 2, M = 4) ( input timer555, RAM_button, //input [N-1:0] adr, input [M-1:0] data_in, output [M-1:0] RAM_out ); reg [1:0]counter; //  always @(posedge timer555) //    counter <= counter + 1; //    1 wire [N-1:0] adr; assign adr = counter; //       reg [M-1:0] mem [2**N-1:0]; always @(posedge RAM_button) mem [adr] <= data_in; assign RAM_out = mem[adr]; endmodule 

在这里,当描述计数器和内存时,使用非阻塞分配<=分配运算符在marsohod.org上进行了讨论
柜台的描述在marsohod.org上

将下载功能添加到计数器。
下载是通过Counter_load命令完成的:
 //input Counter_load; wire [3:0] branch_adr; //   assign branch_adr = data_in; always @(posedge timer555) begin if(Counter_load) //  "Counter_load"    "branch_adr" counter <= branch_adr; else counter <= counter + 1; end 


在一个单独的模块中,创建一个4位寄存器(电池):
 module register4 ( input [3:0] reg_data, input reg_button, output reg [3:0] q ); always @(posedge reg_button) q <= reg_data; endmodule 

将累加器, MUX2多路复用器和求和器加到通用电路中。
加法器将存储器中的电池Acc编号添加到该编号中。
给多路复用器的信号输入编号为data_insum
接下来,将来自MUX2多路复用器的号码加载到Acc电池中:
 module R2 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4) ( input timer555, Counter_load, RAM_button, input MUX_switch, input Acc_button, input [3:0] data_in, output [3:0] Acc, output [DATA_WIDTH-1:0] RAM, output reg [1:0] counter ); wire [1:0] branch_adr; assign branch_adr = data_in[1:0]; //Counter always @(posedge timer555) begin if(Counter_load) counter <= branch_adr; else counter <= counter + 1; end wire [ADDR_WIDTH-1:0] adr; assign adr = counter; //RAM reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; always @(posedge RAM_button) mem [adr] <= Acc; assign RAM = mem[adr]; //sum wire [3:0] sum; assign sum = Acc + RAM; //MUX reg [3:0] MUX2; always @* // Always @* —  «» MUX2 = MUX_switch ? sum : data_in; //Accumulator register4 Acc_reg( .reg_data(MUX2), .reg_button(Acc_button), .q(Acc) ); endmodule 

@ *始终表示“总是”。 一些合成器不了解此设计。 也可以在不使用Always @ *的情况下编写多路复用器(此处仅作为示例)。


减法


为了执行减法,必须在附加代码中提供一个减数 。 您可以在第1.4.6二进制数的符号 “数字电路和计算机体系结构”教科书(David M. Harris和Sarah L. Harris)中阅读有关二进制数的加法和减法。

在主模块中添加一个元素,该元素将从电池中的数字减去存储在内存中的数字:
 wire [3:0] subtract; assign subract = Acc - RAM ; 

将2输入多路复用器替换为4输入:
 always @* MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract) : (MUX_switch[0] ? sum : data_in); 

我们将输出设备连接到电池(4bit'ny寄存器),还将两个标志连接到电池:

1.标志“ 0”是一个日志。 元素4或不。 如果Ass的内容为零,则引发该标志。

2.标志“零或正数”是一个日志。 元素不在4位电池的高电平上。 如果Ass的内容大于或等于零,则引发该标志。

 // "" output Z_flag; assign Z_flag = ~(|Acc); // 4-  - // "   " output PZ_flag; assign PZ_flag = ~Acc[3]; 


4或不
在这里,我们将多输入阀OR NOT描述为〜(| Acc)
Verilog还支持一组门类型。

为逻辑门定义了以下关键字:和(AND),nand(AND-NOT)或(OR),也不(OR-NOT),xor(Exclusive OR),xnor(Exclusive OR-NOT),buf(Buffer element) ,而不是(否定,否)。

在Verilog中,使用门时,必须指定元素的输入和输出,以及(可选)门的名称。 例如,和和或阀必须具有一个输出和两个或多个输入。 因此,对于非阀,我们有
也不命名 list_of_参数
也不是mynor(out,in0,in1,in2,in3);




新增三支队伍

1.将电池内容装入data_out输出设备
2.如果标志“零”被引发,则将地址加载到计数器中(如果Acc = 0,则为JMP
3.如果出现标志“零或正数”,则将地址加载到计数器中(如果Acc > = 0,则为JMP

 module R3 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4) ( input timer555, RAM_button, input JMP, Z_JMP, PZ_JMP, input [1:0] MUX_switch, input Acc_button, input Output_button, input [3:0] data_in, output [3:0] Acc, output [3:0] data_out, output [DATA_WIDTH-1:0] RAM, output Z_flag, PZ_flag, output reg [1:0] counter ); wire [1:0] branch_adr; assign branch_adr = data_in[1:0]; wire Z,PZ; assign Z = Z_flag & Z_JMP; assign PZ = PZ_flag & PZ_JMP; //Counter always @(posedge timer555) begin if(JMP|Z|PZ) counter <= branch_adr; else counter <= counter + 1; end wire [ADDR_WIDTH-1:0] adr; assign adr = counter; //RAM reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; always @(posedge RAM_button) mem [adr] <= Acc; assign RAM = mem[adr]; //sum wire [3:0] sum; assign sum = Acc + RAM; //subtract wire [3:0] subtract; assign subtract = Acc - RAM; //MUX reg [3:0] MUX4; always @* MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract) : (MUX_switch[0] ? sum : data_in); register4 Acc_reg( .reg_data(MUX4), .reg_clk(Acc_button), .q(Acc) ); register4 Output_reg( .reg_data(Acc), .reg_clk(Output_button), .q(data_out) ); assign Z_flag = ~(|Acc); assign PZ_flag = ~Acc[3]; endmodule 



我们将命令和地址放在一个RAM / RAM中,将数据放在另一个RAM / RAM中。



该方案可以从这里下载。

前八位存储命令,后四位将地址加载到计数器中。

通常,应根据时钟衰减,在切换MUX多路复用器 (用于ADDSUBLDA命令)之后,将一个数字加载到Ass电池中。

T.O. 在我们的计算机中,以下命令系统

48x-从RAM向屁股添加一个数字
50x-SUB从Ass中减去存储在RAM中的数字
80x-STA将电池的号码保存到地址x的RAM
58x-LDA从地址中的地址x加载一个数字
04x-BRA无条件过渡到地址为x的单元
02x-如果Ass = 0,则将BRZ转换到地址为x的单元(条件转换)
01x-如果Ass> = 0,则BRP转换到地址为x的单元(条件转换)
40x-INP从Ass中的data_input加载一个数字
20x-OUT将编号从Ass加载到data_out

我们将没有HLT团队

例如,从站点http://peterhigginson.co.uk/LMC/找到最多两个数字的算法

该算法的工作方式如下:我们将data_in中的两个数字存储在数据存储器中。 从第二个数字中减去第一个:

  • 如果结果为负,则在Ass中写入第一个数字,在data_out中将Ass中的数字写入;
  • 如果结果是肯定的,则在Ass中写入第二个数字,在data_out中写入来自Ass的数字。

00 INP
01 STA 11
02 INP
03 STA 12
04 SUB 11
05 BRP 08
06 LDA 11
07 BRA 09
08 LDA 12
09 OUT


在我们的命令系统中,此算法将如下所示

400
80b
400
80c
50b
018
58b
049
58c
200



将数据加载到计数器所需的计数器控制输入处的NOT元素就是Logisim程序的这种功能;在实际方案中,不需要控制输入处的NOT元素(至少我不知道这样的计数器)。

可以从官方网站下载Quartus II。

在“我的主要工作功能”下注册为*时,选择“学生”。
接下来,您需要下载编程器的驱动程序(可从C:\ altera \ ... \ quartus \ drivers \ usb-blaster安装usb-blaster的驱动程序)。

Logisim可在此处下载。

Source: https://habr.com/ru/post/zh-CN412057/


All Articles