// ALU controls reg [2:0] alu_op; wire [7:0] alu_result; wire alu_zero;
// Memory interface assign addr_bus = (state == FETCH) ? pc : ((state == MEM_READ || state == MEM_WRITE) ? ir[7:0], reg_b : 16'hzzzz); assign data_bus = (state == MEM_WRITE) ? reg_a : 8'hzz; assign mem_read = (state == FETCH || state == MEM_READ); assign mem_write = (state == MEM_WRITE);
reg_file reg_inst (.clk(clk), .rst(rst), .reg_sel_a(reg_sel_a), .reg_sel_b(reg_sel_b), .reg_sel_wr(reg_sel_wr), .wr_data(wr_data), .wr_en(wr_en), .reg_a_out(reg_a), .reg_b_out(reg_b)); 8-bit microprocessor verilog code
always @(posedge clk or posedge rst) begin if (rst) begin registers[0] <= 8'h00; registers[1] <= 8'h00; registers[2] <= 8'h00; registers[3] <= 8'h00; end else if (wr_en) begin registers[reg_sel_wr] <= wr_data; end end endmodule module processor ( input clk, rst, output [15:0] addr_bus, inout [7:0] data_bus, output mem_read, mem_write ); // Internal signals reg [15:0] pc; reg [7:0] ir; reg [7:0] alu_out; reg zero_flag; // Register selects and controls reg [1:0] reg_sel_a, reg_sel_b, reg_sel_wr; reg [7:0] wr_data; reg wr_en; wire [7:0] reg_a, reg_b;
// Control FSM states reg [2:0] state; localparam FETCH = 3'b000, DECODE = 3'b001, EXECUTE = 3'b010, MEM_READ = 3'b011, MEM_WRITE = 3'b100; // ALU controls reg [2:0] alu_op; wire [7:0]
always #5 clk = ~clk;
module processor_tb; reg clk, rst; wire [15:0] addr; wire [7:0] data; wire mem_read, mem_write; processor uut (.clk(clk), .rst(rst), .addr_bus(addr), .data_bus(data), .mem_read(mem_read), .mem_write(mem_write)); reg_a : 8'hzz; assign mem_read = (state ==
// Main control logic always @(posedge clk or posedge rst) begin if (rst) begin pc <= 16'h0000; ir <= 8'h00; state <= FETCH; wr_en <= 1'b0; end else begin case (state) FETCH: begin ir <= data_bus; // Instruction fetch pc <= pc + 1; state <= DECODE; end DECODE: begin wr_en <= 1'b0; case (ir[7:4]) // Opcode in upper nibble 4'b0001: begin // MOV A, B reg_sel_wr <= 2'b00; wr_data <= reg_b; wr_en <= 1'b1; state <= FETCH; end 4'b0010: begin // ADD A, B reg_sel_a <= 2'b00; reg_sel_b <= 2'b01; alu_op <= 3'b000; state <= EXECUTE; end 4'b0011: begin // SUB A, B reg_sel_a <= 2'b00; reg_sel_b <= 2'b01; alu_op <= 3'b001; state <= EXECUTE; end 4'b0100: begin // JMP pc <= ir[3:0], data_bus; state <= FETCH; end 4'b0101: begin // JZ if (zero_flag) pc <= ir[3:0], data_bus; else state <= FETCH; end 4'b0110: begin // LD A, [addr] state <= MEM_READ; end 4'b0111: begin // ST [addr], A state <= MEM_WRITE; end 4'b1000: state <= FETCH; // HLT default: state <= FETCH; endcase end EXECUTE: begin reg_sel_wr <= 2'b00; // Write back to A wr_data <= alu_result; wr_en <= 1'b1; zero_flag <= alu_zero; state <= FETCH; end MEM_READ: begin reg_sel_wr <= 2'b00; wr_data <= data_bus; wr_en <= 1'b1; state <= FETCH; end MEM_WRITE: begin state <= FETCH; end endcase end end endmodule Here's a simple testbench to run a few instructions:
Have you built your own CPU in Verilog? Share your experience or questions in the comments!
// Instantiate modules alu alu_inst (.a(reg_a), .b(reg_b), .op(alu_op), .result(alu_result), .zero(alu_zero));