//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ // CPU //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ module CPU_BN16( output [15:0]a, // Bus de direcciones output [15:0]do, // Bus de datos de salida output wr, // Escritura si 1, lectura si 0 input [15:0]di, // Bus de datos de entrada input clk, // Entrada de Reloj input resetb // Entrada de Reset, activa en bajo ); wire [15:0] busa; wire [15:0] busb; wire [15:0] busd; wire [15:0] buspc; wire [15:0] busimm; wire [15:0] busop; wire [2:0]sela; // Selección de registro para lectura a bus A wire [2:0]selb; // Selección de registro para lectura a bus B wire [2:0]seld; // Selección de registro para escritura wire wren; // Habilitación de escritura en registro wire incpc; // Incrementar PC wire ld; // Instrucción LD (lectura de memoria) wire st; // Instrucción ST (escritura en memoria) wire jr; // Instrucción de salto (para operandos inmediatos) wire imm; // Operandos inmediatos a bus B si 1 wire wrzs; // Escribir en flags Z y S wire wrc; // Escribir en flag C wire [2:0]aluop; // Operación de la ALU wire aluzab; // Forzar entrada A a 0 en la ALU si 0 wire aluib; // Invertir bits de entrada B de la ALU si 1 wire [1:0]cisel; // Selección del acarreo de entrada a la ALU wire [15:0]op; // Código de operación desde registro de instrucción wire cf; // Entrada de Flag de Acarreo wire zf; // Entrada de Flag de Zero wire sf; // Entrada de Flag de Signo wire icf; // Entrada al Flag C desde la ALU wire fnop; // Entrada de forzar NOPs a registro de instrucción // Instancias de bloques ALU alu1(.f(busd),.co(icf),.a(busa),.b(busb), .ci(cf),.op(aluop),.zab(aluzab),.ib(aluib),.cisel(cisel)); REGBANK regbk1(.a(busa),.b(do),.pco(buspc),.d(busd), .asel(sela),.bsel(selb),.dsel(seld), .wren(wren),.inc(incpc),.clk(clk),.resb(resetb) ); FLAGS flags1( .qc(cf),.qz(zf),.qs(sf),.f(busd),.ci(icf),.wrzs(wrzs),.wrc(wrc),.clk(clk) ); IR ireg1( .q(busop), .d(di), .fnop(fnop), .clk(clk),.resb(resetb) ); IMM imm1( .f(busimm), .op(busop), .jr(jr) ); DECODING decoding1( .sela(sela), .selb(selb), .seld(seld), .wren(wren), .incpc(incpc), .ld(ld), .st(st), .jr(jr), .imm(imm), .wrzs(wrzs), .wrc(wrc), .aluop(aluop), .aluzab(aluzab), .aluib(aluib), .cisel(cisel), .op(busop), .cf(cf), .zf(zf), .sf(sf) ); assign fnop = (ld | st ); assign a = fnop? busa : buspc; assign busb = ld ? di : (imm? busimm : do); assign wr = st; endmodule //------------------------------------------------------------------------------------------ // ALU //------------------------------------------------------------------------------------------ module ALU ( output [15:0]f, // Salida de resultado output co, // Salida de acarreo input [15:0]a, // Entrada de operando A input [15:0]b, // Entrada de operando B input ci, // Entrada de acarreo input [2:0]op, // Operación (0: suma, 1: AND, 2: OR, 3: XOR, 4: ROR) input zab, // Forzar operando A=0 si 0 input ib, // Invertir bits de operando B si 1 input [1:0]cisel // Selección de acarreo de entrada: (0:L, 1:H, 2:ci, 3:~ci) ); wire [15:0]sa; wire [15:0]sb; reg [15:0]f; // No realmente registros reg sco; // Multiplexor de acarreo de entrada reg sci; always@* case (cisel) 0 : sci = 0; 1 : sci = 1; 2 : sci = ci; 3 : sci = ~ci; endcase // Datos intermedios assign sa= zab ? a : 16'b0; assign sb= ib ? ~b : b; // Operación always@* case (op) 0 : {sco,f} = sa+sb+sci; // SUMA 1 : {sco,f} = {1'bx,sa & sb}; // AND 2 : {sco,f} = {1'bx,sa | sb}; // OR 3 : {sco,f} = {1'bx,sa ^ sb}; // XOR 4 : {sco,f} = {sb[0],ci,sb[15:1]}; // ROR default : {sco,f} ={1'bx,16'bxxxxxxxxxxxxxxxx}; endcase assign co = ib ? ~sco : sco; endmodule //------------------------------------------------------------------------------------------ // Banco de Registros (PC incluido) //------------------------------------------------------------------------------------------ module REGBANK ( output [15:0]a, // Salida para bus A (ALU, dir memoria) output [15:0]b, // Salida para bus B (ALU, datos memoria) output [15:0]pco, // Salida del contador de programa (R0) input [15:0]d, // Entrada de datos al banco input [2:0]asel, // Selección de registro para lectura a bus A input [2:0]bsel, // Selección de registro para lectura a bus B input [2:0]dsel, // Selección de registro para escritura input wren, // Habilitación de escritura si 1 input inc, // Incrementar PC si 1 input clk, input resb ); wire [7:0]wr; assign wr[0]=wren & (~dsel[2]) & (~dsel[1]) & (~dsel[0]); assign wr[1]=wren & (~dsel[2]) & (~dsel[1]) & ( dsel[0]); assign wr[2]=wren & (~dsel[2]) & ( dsel[1]) & (~dsel[0]); assign wr[3]=wren & (~dsel[2]) & ( dsel[1]) & ( dsel[0]); assign wr[4]=wren & ( dsel[2]) & (~dsel[1]) & (~dsel[0]); assign wr[5]=wren & ( dsel[2]) & (~dsel[1]) & ( dsel[0]); assign wr[6]=wren & ( dsel[2]) & ( dsel[1]) & (~dsel[0]); assign wr[7]=wren & ( dsel[2]) & ( dsel[1]) & ( dsel[0]); // Registro R0 (Contador de Programa) wire [15:0]pci; assign pci = wr[0] ? d : (pco+inc) ; REG16R r0(.q(pco),.d(pci),.resb(resb),.clk(clk)); // Registros R1-R7 wire [15:0]q1; wire [15:0]q2; wire [15:0]q3; wire [15:0]q4; wire [15:0]q5; wire [15:0]q6; wire [15:0]q7; REG16 r1(.q(q1),.d(d),.wr(wr[1]),.clk(clk)); REG16 r2(.q(q2),.d(d),.wr(wr[2]),.clk(clk)); REG16 r3(.q(q3),.d(d),.wr(wr[3]),.clk(clk)); REG16 r4(.q(q4),.d(d),.wr(wr[4]),.clk(clk)); REG16 r5(.q(q5),.d(d),.wr(wr[5]),.clk(clk)); REG16 r6(.q(q6),.d(d),.wr(wr[6]),.clk(clk)); REG16 r7(.q(q7),.d(d),.wr(wr[7]),.clk(clk)); // Multiplexores para buses A y B reg [15:0]a; // No son registros reg [15:0]b; always@* case (asel) 0 : a <= pco; 1 : a <= q1; 2 : a <= q2; 3 : a <= q3; 4 : a <= q4; 5 : a <= q5; 6 : a <= q6; 7 : a <= q7; endcase always@* case (bsel) 0 : b <= pco; 1 : b <= q1; 2 : b <= q2; 3 : b <= q3; 4 : b <= q4; 5 : b <= q5; 6 : b <= q6; 7 : b <= q7; endcase endmodule // Registro simple con habilitación de escritura para instanciar en el banco module REG16 (output [15:0]q, input [15:0]d, input wr, input clk); reg [15:0]q; always @(posedge clk) q<= wr ? d : q; endmodule // Registro simple con reset para instanciar en el banco module REG16R (output [15:0]q, input [15:0]d, input resb, input clk); reg [15:0]q; always @(posedge clk or negedge resb ) if (!resb) q<=16'h0000; else q<= d; endmodule //------------------------------------------------------------------------------------------ // Operandos inmediatos //------------------------------------------------------------------------------------------ module IMM( output [15:0]f, // Salida de operando inmediato input [15:0]op, // Entrada desde reg. de instrucción input jr // Tipo de dato (0: 3bit + zero fill, 1: 9bit + extensión signo) ); assign f= jr ? {op[8],op[8],op[8],op[8],op[8],op[8],op[8], op[8:0]} // extensión de signo : {13'b0000000000000,op[2:0]}; // Zero fill endmodule //------------------------------------------------------------------------------------------ // Registro de instrucción //------------------------------------------------------------------------------------------ module IR ( output [15:0]q, // Salida (hacia operandos immediatos y decodificación) input [15:0]d, // Entrada (desde bus de datos) input fnop, // fetch NOP si 1 (para instrucciones LD y ST) input clk, input resb ); reg [15:0]q=0; wire [15:0]di; assign di = {d[15:12],(~fnop)&d[11],(~fnop)&d[10],(~fnop)&d[9], d[8:0]}; always @(posedge clk or negedge resb) if (!resb) q<=16'h0000; else q<= di; endmodule //------------------------------------------------------------------------------------------ // FLAGS //------------------------------------------------------------------------------------------ module FLAGS ( output qc, // Salida de flag C output qz, // Salida de flag Z output qs, // Salida de flag S input [15:0]f, // Entrada de resultado de ALU input ci, // Entrada de flag C input wrzs, // Escribir flags Z y S si 1 input wrc, // Escribir flag C si 1 input clk ); reg qc,qz,qs; // Flag Z wire zi; assign zi=~(f[15]|f[14]|f[13]|f[12]|f[11]|f[10]|f[9]|f[8]| f[7]|f[6]|f[5]|f[4]|f[3]|f[2]|f[1]|f[0] ); always @(posedge clk) begin if (wrc) qc<=ci; if (wrzs) begin qz<=zi; qs<=f[15]; end end endmodule //------------------------------------------------------------------------------------------ // Decodificación de instrucciones //------------------------------------------------------------------------------------------ module DECODING( output [2:0]sela, // Selección de registro para lectura a bus A output [2:0]selb, // Selección de registro para lectura a bus B output [2:0]seld, // Selección de registro para escritura output wren, // Habilitación de escritura en registro output incpc, // Incrementar PC output ld, // Instrucción LD (lectura de memoria) output st, // Instrucción ST (escritura en memoria) output jr, // Instrucción de salto (para operandos inmediatos) output imm, // Operandos inmediatos a bus B si 1 output wrzs, // Escribir en flags Z y S output wrc, // Escribir en flag C output [2:0]aluop, // Operación de la ALU output aluzab, // Forzar entrada A a 0 en la ALU si 0 output aluib, // Invertir bits de entrada B de la ALU si 1 output [1:0]cisel, // Selección del acarreo de entrada a la ALU input [15:0]op, // Código de operación desde registro de instrucción input cf, // Entrada de Flag de Acarreo input zf, // Entrada de Flag de Zero input sf // Entrada de Flag de Signo ); reg exe; // Ejecutar / No ejecutar (no es un FF) // multiplexor de 8 a 1 para exe always@* case (op[11:9]) 0 : exe <= 0; 1 : exe <= 1; 2 : exe <= ~cf; 3 : exe <= cf; 4 : exe <= ~zf; 5 : exe <= zf; 6 : exe <= ~sf; 7 : exe <= sf; endcase // Selección de registros assign sela = jr ? 3'b000 : op[5:3]; assign selb = op[2:0]; assign seld = jr ? 3'b000 : op[8:6]; // Ciertos OP-codes y líneas de control assign ld = exe & op[15] & op[14] &(~op[13]) &(~op[12]); assign st = exe & op[15] & op[14] &(~op[13]) & op[12]; assign jr = exe & op[15] & op[14] & op[13] &(~op[12]); assign imm = jr | ( (~op[15]) & op[14] &(~op[13])); assign wren = exe & (~st); assign incpc = (~(ld |st)) | (~(sela[2]|sela[1]|sela[0])); // Control de ALU reg [2:0]aluop; reg aluzab, aluib; reg [1:0] cisel; reg twrc, twrzs; always@* case (op[15:12]) 0 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b1 ,1'b0 ,2'b00, 1'b1, 1'b1}; // ADD 1 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b1 ,1'b1 ,2'b01, 1'b1, 1'b1}; // SUB 2 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b1, 1'b0, 2'b10, 1'b1, 1'b1}; // ADC 3 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b1, 1'b1, 2'b11, 1'b1, 1'b1}; // SBB 4 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b1, 1'b0, 2'b00, 1'b1, 1'b1}; // ADDQ 5 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b1, 1'b1, 2'b01, 1'b1, 1'b1}; // SUBQ 6 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b010, 1'b0, 1'b1, 2'bxx, 1'b1, 1'b1}; // NOT 7 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b0, 1'b1, 2'b01, 1'b1, 1'b1}; // NEG 8 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b001, 1'b1, 1'b0, 2'bxx, 1'b0, 1'b1}; // AND 9 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b010, 1'b1, 1'b0, 2'bxx, 1'b0, 1'b1}; // OR 10 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b011, 1'b1, 1'b0, 2'bxx, 1'b0, 1'b1}; // XOR 11 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b100, 1'b1, 1'b0, 2'b10, 1'b1, 1'b1}; // ROR 12 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b010, 1'b0, 1'b0, 2'bxx, 1'b0, 1'b1}; // LD 13 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'bxxx, 1'bx, 1'bx, 2'bxx, 1'b0, 1'b0}; // ST 14 : {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'b000, 1'b1, 1'b0, 2'b00, 1'b0, 1'b0}; // JR default: {aluop,aluzab,aluib,cisel,twrc,twrzs} <= {3'bxxx,1'bx,1'bx,2'bxx,1'bx,1'bx}; // No implementadas endcase // Escritura en flags assign wrc = exe & twrc & (seld[2]|seld[1]|seld[0]); assign wrzs = exe & twrzs & (seld[2]|seld[1]|seld[0]); endmodule