Bienvenido a un nuevo proyecto sobre Verilog con la tarjeta BASYS 3. En este capítulo iniciamos una serie de tutoriales en los que veremos cómo hacer una CPU sencilla con FPGA.
Como todo proyecto de cierta dificultad, vamos a dividirlo en circuitos sencillos que probaremos por separado.
En todo diseño de CPU, se hace necesaria la implementación de un bus de datos que comunique todos los registros. En esta primera parte, veremos cómo describir el bus de datos.
El esquema de la imagen, representa la arquitectura de muestra CPU, formada por dos registros A y B, un bus de datos implementado con un multiplexor y la ALU.
El display de 7 segmentos servirá para visualizar el contenido de los diferentes buses de datos.
El bus de datos permite introducir información desde el exterior, comparte con otros registros el resultado de la ALU y permite el intercambio de datos entre los diferentes registros.
Hay diferentes formas de implementar buses de datos en Verilog, como por ejemplo mediante buffers I/O triestado, sin embargo, el uso de multiplexores es tremendamente fácil de implementar y ya que se trata de hacer una CPU sencilla, la elección está clara.
Como decía al principio, en este tutorial veremos como implementar el bus de datos. Para que sea lo más sencillo posible, vamos a eliminar los componentes no esenciales, como la ALU y los registros, los cuales sustituiremos por un valor fijo.
El arbitraje del bus lo implementamos mediante un encoder conectado a las entradas del multiplexor. En función del switch que activemos en la BASYS 3, el encoder codificará las salidas s0 y s1 para que el multiplexor ponga en el bus el dato de aquel bus que solicita acceso.
Para describir el encoder emplearemos una estructura case. Las entradas son las señales enable de los buses de datos y la salida son las señales de selección s0 y s1 que aplicaremos al multiplexor del bus.
Conectaremos las entradas y salidas a los ledes de la BASYS 3 para verificar que el encoder funciona como se espera.
El código Verilog queda como sigue:
El multiplexor del bus de datos tiene un ancho de 8 bits. Las entradas son los buses de datos de los registros A y B, la entrada de datos del exterior DATA_IN y la salida de la ALU. Las entradas de selección se conectan a las salidas del encoder.
El código Verilog del multiplexor del bus de datos queda como sigue:
Emplearemos el display de 7 segmentos de la BASYS 3 para visualizar el contenido del bus de datos y verificar que todo funciona correctamente.
Este driver se ha utilizado en proyectos anteriores. Básicamente consiste en una señal de reloj customizada, aplicada a un contador de dos bits que va conmutando los dígitos del display.
En el módulo top del bus de datos describimos las interconexiones de los distintos módulos simples que forman nuestro diseño.
module data_bus_top (clk, a_enable, b_enable, alu_enable, data_in_enable,
led0, led1, led2, led3, led4, led5, seg, an);
input clk, a_enable, b_enable, alu_enable, data_in_enable; // generic inputs
output [6:0] seg; // segmentos
output [3:0] an; // anodos
output led0, led1, led2, led3, led4, led5; // ledes de status
// declaracion de los buses
wire [7:0] data_bus, a_reg_bus, b_reg_bus, ssd_bus, alu_reg_bus;
wire w30, w31;
// asignacion de los ledes de status
assign led0 = data_in_enable;
assign led1 = a_enable;
assign led2 = b_enable;
assign led3 = alu_enable;
assign led4 = w30;
assign led5 = w31;
// para simular los registros, asignamos un valor fijo a los buses de datos.
assign data_in_bus = 8'b0000_0011;
assign a_reg_bus = 8'b0000_0101;
assign b_reg_bus = 8'b0000_0111;
assign alu_reg_bus = 8'b0000_1001;
//******************
// 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_in_bus),
.out(data_bus));
// 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 segment display driver
ssd_driver SSDD (
.in(ssd_bus),
.seg(seg));
endmodule
El archivo .XDC de la BASYS 3 queda como se muestra en la imagen siguiente:
Programamos la BASYS 3 y lo que vemos es una imagen como la siguiente, donde cada uno de los displays de 7 segmentos muestra un dato; de izquierda a derecha, el bus de datos, los registros A y B y el cuarto es el resultado de la ALU.
Accionando los switches SW0, SW1, SW2 y SW3 ponemos en bus el dato del registro correspondiente.
Los ledes 0 a 3 indican qué bus de datos estamos habilitando, mientras que los ledes 4 y 5 son las salidas del encoder que aplicamos al multiplexor del bus.
Puedes descargar el código Verilog aquí abajo.