This time, I implemented a new class of instructions: MOV, for setting a register to a value of another register.
I have also changed all instances of blocking assignment “=” to the non-blocking assignment, “<=” — to better simulate behavior of real registers. This way the display statements printing the values of registers A, B, C, etc. will more closely correspond to the actual value of the register during that tick, and the new value will not be printed until the next tick.
Last but not least, if you look at the code you will notice how messy and unreadable it became. So far, to keep the story simple, I kept the code structure as flat as possible. Next time I should split things up in more manageable logic blocks. Specifically, the case statements on register specifier fields in the instruction, should become a reusable function or perhaps even a separate module.
As always, I removed the old comments and commented the new and updated code lines:
module CPU(input iClk, input iRst, output [7:0] oAddressBus, input [7:0] i
localparam MVI=2'b00, MOV=2'b01, ALU=2'b10, ALI=2'b11;
localparam B = 3'b000, C = 3'b001, D = 3'b010, E = 3'b011,
H = 3'b100, L = 3'b101, M = 3'b110, A = 3'b111;
reg [7:0] _pc;
reg _mode;
localparam NORMAL=1'b0, READ_IMMED=1'b1;
reg [7:0] _instruction;
reg [7:0] _a, _b, _c, _d, _e, _h, _l;
always @(posedge iClk, posedge iRst)
begin
if(iRst)
begin
$display("resetting...");
//In real life registers don't take new value
//until the raising edge of the next clock tick.
//In Verilog, such behavior is simulated
//with non-blocking assignment, "<="
_pc <= 8'h0;
_mode <= NORMAL;
//setting all registers to zero on reset
_a <= 8'h0;
_b <= 8'h0;
_c <= 8'h0;
_d <= 8'h0;
_e <= 8'h0;
_h <= 8'h0;
_l <= 8'h0;
end
else begin
$display("PC:%d", _pc);
$display("mem:%d", iMemoryValue);
case(_mode)
NORMAL:
if(iMemoryValue == 8'h0) $display("NOP");
else case(iMemoryValue[7:6])
MVI: if(iMemoryValue[2:0] == M) begin
$display("initiating MVI");
_mode <= READ_IMMED;
_instruction <= iMemoryValue;
end
else $display("unknown instruction");
//detecting a MOV-class instruction
MOV:
//register fields of the instruction
//specify source and destination
case(iMemoryValue[5:0])
//using {} syntaxis to concatenate
//the constants A and B
{A, B}: begin
$display("MOV A, B");
_a <= _b;
end
{B, A}: begin
$display("MOV B, A");
_b <= _a;
end
//TODO: add {A, C} etc.
//not supporting other combinations yet
default: $display("unknown instruction");
endcase
default:
$display("unknown instruction");
endcase
READ_IMMED: begin
case(_instruction[5:3])
B: begin
$display("MVI B, %d", iMemoryValue);
_b <= iMemoryValue;
end
C: begin
$display("MVI C, %d", iMemoryValue);
_c <= iMemoryValue;
end
D: begin
$display("MVI D, %d", iMemoryValue);
_d <= iMemoryValue;
end
E: begin
$display("MVI E, %d", iMemoryValue);
_e <= iMemoryValue;
end
H: begin
$display("MVI H, %d", iMemoryValue);
_h <= iMemoryValue;
end
L: begin
$display("MVI L, %d", iMemoryValue);
_l <= iMemoryValue;
end
M: $display("unknown instruction");
A: begin
$display("MVI A, %d", iMemoryValue);
_a <= iMemoryValue;
end
endcase
_mode <= NORMAL;
end
endcase
//more debug prints
$display("A=%d", _a);
$display("B=%d", _b);
$display("C=%d", _c);
$display("D=%d", _d);
$display("E=%d", _e);
$display("H=%d", _h);
$display("L=%d", _l);
_pc <= _pc + 1'b1;
end
end
assign oAddressBus = _pc;
endmodule
module ROM(input [7:0] iAddress, output [7:0] oReadPort);
reg [7:0] _rom [0:255];
initial
begin
_rom[0] = 0; //NOP
//verilog allows underscores in numeric literals,
//to improve readability
_rom[1] = 8'b00_000_110; //MVI B, 55
_rom[2] = 55;
_rom[3] = 8'b00_001_110; //MVI C, 255
_rom[4] = 255;
_rom[5] = 8'b01_111_000; //MOV A, B
//etc.
end
assign oReadPort = _rom[iAddress];
endmodule
And here’s the printout (only showing clock ticks 3 to 10):
--------------Tick 3--------------- PC: x mem: x A= x B= x C= x D= x E= x H= x L= x resetting... --------------Tick 4--------------- PC: 0 mem: 0 executing NOP! A= 0 B= 0 C= 0 D= 0 E= 0 H= 0 L= 0 --------------Tick 5--------------- PC: 1 mem: 6 initiating MVI A= 0 B= 0 C= 0 D= 0 E= 0 H= 0 L= 0 --------------Tick 6--------------- PC: 2 mem: 55 MVI B, 55 A= 0 B= 0 C= 0 D= 0 E= 0 H= 0 L= 0 --------------Tick 7--------------- PC: 3 mem: 14 initiating MVI A= 0 B= 55 C= 0 D= 0 E= 0 H= 0 L= 0 --------------Tick 8--------------- PC: 4 mem:255 MVI C, 255 A= 0 B= 55 C= 0 D= 0 E= 0 H= 0 L= 0 --------------Tick 9--------------- PC: 5 mem:120 MOV A, B A= 0 B= 55 C=255 D= 0 E= 0 H= 0 L= 0 --------------Tick 10--------------- PC: 6 mem: x unknown instruction A= 55 B= 55 C=255 D= 0 E= 0 H= 0 L= 0
