6 de abril de 2025
Llegamos por fin al tercer y último tutorial sobre cómo hacer una CPU sencilla con una FPGA. En esta tercera parte veremos cómo hacer la ALU y pondremos en conjunto lo visto en los tutoriales anteriores, es decir, el Bus de Datos y los Registros.
El código Verilog de la ALU está basado en este tutorial, al que he quitado algunas líneas para simplificar.
La siguiente imagen muestra la arquitectura de la CPU, ésta incluye el bus de datos, la ALU, los registros y el driver para el display de 7 segmentos.
En el módulo top instanciamos los módulos simples que forman la CPU.
//////////////////////////////////////////////////////////////////////////////////
//
// Engineer: Angel M.
// Create Date: 6 de abril de 2025
// Module Name: sap_alu_top
// Project Name: CPU sencilla con BASYS 3
// Target Devices: Basys 3
// Tool Versions: vivado
// Description: ALU sencilla, top module
//
//////////////////////////////////////////////////////////////////////////////////
module sap_alu_top (clk, reset, a_latch, b_latch, a_enable, b_enable, alu_enable, data_in_enable, alu_sub, data_sw, led0, led1, led2, led3, led6, led7, seg, an, alu_C, alu_Z);
input clk, reset; // generic inputs
input a_latch, a_enable; // a reg inputs
input b_latch, b_enable; // b reg inputs
input alu_enable, alu_sub, data_in_enable;
input [7:0] data_sw;
//input [3:0] opcore;
output alu_C, alu_Z;
output [6:0] seg;
output [3:0] an;
output led0, led1, led2, led3, led6, led7;
wire [7:0] data_bus, a_reg_bus, b_reg_bus, ssd_bus, alu_reg_bus;
wire w30, w31;
assign led0 = data_in_enable;
assign led1 = a_enable;
assign led2 = b_enable;
assign led3 = alu_enable;
assign led6 = w30;
assign led7 = w31;
//******************
// Structural coding
//******************
// encoder para seleccion de bus de datos
encoder ENC (
.in0(data_in_enable),
.in1(a_enable),
.in2(b_enable),
.in3(alu_enable),
.s0(w30),
.s1(w31));
// mux 4-1 para bus de datos
mux_bus MB (
.s0(w30),
.s1(w31),
.in3(alu_reg_bus),
.in2(b_reg_bus),
.in1(a_reg_bus),
.in0(data_sw),
.out(data_bus));
// a register
sap_register a_register (
.clk(clk),
.reset(reset),
.DATA_IN(data_bus),
.latch(a_latch),
.REG_OUT(a_reg_bus));
// b register
sap_register b_register (
.clk(clk),
.reset(reset),
.DATA_IN(data_bus),
.latch(b_latch),
.REG_OUT(b_reg_bus));
// ALU
sap_alu ALU (
.enable(alu_enable),
.clk(clk),
.sub(alu_sub),
.a_reg_data(a_reg_bus),
.b_reg_data(b_reg_bus),
.DATA(alu_reg_bus),
.C(alu_C),
.Z(alu_Z));
//.opcore(opcore),
// mux for seven segment display
mux4_1 MUX41 (
.clk(clk),
.in_0(alu_reg_bus),
.in_1(b_reg_bus),
.in_2(a_reg_bus),
.in_3(data_bus),
.out(ssd_bus),
.gates(an));
// Seven segnent display driver
ssd_driver SSDD (
.in(ssd_bus),
.seg(seg));
endmodule
El archivo .XDC queda así:
Lo subimos a la BASYS 3 y comprobamos el funcionamiento.
Con los switches sw8-15 introducimos los datos en el bus y con los pulsadores L y R los cargamos en los registros A y B respectivamente.
Al accionar sw3 se habilita la ALU y se muestra el resultado de la operación. Podemos seleccionar entre sumar o restar con el sw4.
Para mostrar correctamente los datos en los displays de 7 segmentos de la BASYS 3, sólo he tenido en cuenta los 4 bits menos significativos del bus.
Primera Parte: El bus de datos
Many thanks to Phil, for sharing his 8 bit computer project.
& 100 Random Task: Create a custom CPU from scratch.
Puedes descargar los archivos Verilog de este tutorial aquí abajo.