mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-19 09:31:24 +00:00
304 lines
7.2 KiB
Verilog
304 lines
7.2 KiB
Verilog
|
|
`ifndef ALU_H
|
|
`define ALU_H
|
|
|
|
// ALU operations
|
|
`define OP_ZERO 4'h0
|
|
`define OP_LOAD_A 4'h1
|
|
`define OP_INC 4'h2
|
|
`define OP_DEC 4'h3
|
|
`define OP_ASL 4'h4
|
|
`define OP_LSR 4'h5
|
|
`define OP_ROL 4'h6
|
|
`define OP_ROR 4'h7
|
|
`define OP_OR 4'h8
|
|
`define OP_AND 4'h9
|
|
`define OP_XOR 4'ha
|
|
`define OP_LOAD_B 4'hb
|
|
`define OP_ADD 4'hc
|
|
`define OP_SUB 4'hd
|
|
`define OP_ADC 4'he
|
|
`define OP_SBB 4'hf
|
|
|
|
// ALU module
|
|
module ALU(A, B, carry, aluop, Y);
|
|
|
|
parameter N = 8; // default width = 8 bits
|
|
input [N-1:0] A; // A input
|
|
input [N-1:0] B; // B input
|
|
input carry; // carry input
|
|
input [3:0] aluop; // alu operation
|
|
output reg [N:0] Y; // Y output + carry
|
|
|
|
always @(*)
|
|
case (aluop)
|
|
// unary operations
|
|
`OP_ZERO: Y = 0;
|
|
`OP_LOAD_A: Y = {1'b0, A};
|
|
`OP_INC: Y = A + 1;
|
|
`OP_DEC: Y = A - 1;
|
|
// unary operations that generate and/or use carry
|
|
`OP_ASL: Y = {A, 1'b0};
|
|
`OP_LSR: Y = {A[0], 1'b0, A[N-1:1]};
|
|
`OP_ROL: Y = {A, carry};
|
|
`OP_ROR: Y = {A[0], carry, A[N-1:1]};
|
|
// binary operations
|
|
`OP_OR: Y = {1'b0, A | B};
|
|
`OP_AND: Y = {1'b0, A & B};
|
|
`OP_XOR: Y = {1'b0, A ^ B};
|
|
`OP_LOAD_B: Y = {1'b0, B};
|
|
// binary operations that generate and/or use carry
|
|
`OP_ADD: Y = A + B;
|
|
`OP_SUB: Y = A - B;
|
|
`OP_ADC: Y = A + B + (carry?1:0);
|
|
`OP_SBB: Y = A - B - (carry?1:0);
|
|
endcase
|
|
|
|
endmodule
|
|
|
|
/*
|
|
Bits Description
|
|
|
|
00ddaaaa A @ B -> dest
|
|
01ddaaaa A @ immediate -> dest
|
|
11ddaaaa A @ read [B] -> dest
|
|
10000001 swap A <-> B
|
|
1001nnnn A -> write [nnnn]
|
|
1010tttt conditional branch
|
|
|
|
dd = destination (00=A, 01=B, 10=IP, 11=none)
|
|
aaaa = ALU operation (@ operator)
|
|
nnnn = 4-bit constant
|
|
tttt = flags test for conditional branch
|
|
*/
|
|
|
|
// destinations for COMPUTE instructions
|
|
`define DEST_A 2'b00
|
|
`define DEST_B 2'b01
|
|
`define DEST_IP 2'b10
|
|
`define DEST_NOP 2'b11
|
|
|
|
// instruction macros
|
|
`define I_COMPUTE(dest,op) { 2'b00, (dest), (op) }
|
|
`define I_COMPUTE_IMM(dest,op) { 2'b01, (dest), (op) }
|
|
`define I_COMPUTE_READB(dest,op) { 2'b11, (dest), (op) }
|
|
`define I_CONST_IMM_A { 2'b01, `DEST_A, `OP_LOAD_B }
|
|
`define I_CONST_IMM_B { 2'b01, `DEST_B, `OP_LOAD_B }
|
|
`define I_JUMP_IMM { 2'b01, `DEST_IP, `OP_LOAD_B }
|
|
`define I_STORE_A(addr) { 4'b1001, (addr) }
|
|
`define I_BRANCH_IF(zv,zu,cv,cu) { 4'b1010, (zv), (zu), (cv), (cu) }
|
|
`define I_CLEAR_CARRY { 8'b10001000 }
|
|
`define I_SWAP_AB { 8'b10000001 }
|
|
`define I_RESET { 8'b10111111 }
|
|
// convenience macros
|
|
`define I_ZERO_A `I_COMPUTE(`DEST_A, `OP_ZERO)
|
|
`define I_ZERO_B `I_COMPUTE(`DEST_B, `OP_ZERO)
|
|
`define I_BRANCH_IF_CARRY(carry) `I_BRANCH_IF(1'b0, 1'b0, carry, 1'b1)
|
|
`define I_BRANCH_IF_ZERO(zero) `I_BRANCH_IF(zero, 1'b1, 1'b0, 1'b0)
|
|
`define I_CLEAR_ZERO `I_COMPUTE(`DEST_NOP, `OP_ZERO)
|
|
|
|
module CPU(clk, reset, address, data_in, data_out, write);
|
|
|
|
input clk;
|
|
input reset;
|
|
output reg [7:0] address;
|
|
input [7:0] data_in;
|
|
output reg [7:0] data_out;
|
|
output reg write;
|
|
|
|
reg [7:0] IP;
|
|
reg [7:0] A, B;
|
|
reg [8:0] Y;
|
|
reg [2:0] state;
|
|
|
|
reg carry;
|
|
reg zero;
|
|
wire [1:0] flags = { zero, carry };
|
|
|
|
reg [7:0] opcode;
|
|
wire [3:0] aluop = opcode[3:0];
|
|
wire [1:0] opdest = opcode[5:4];
|
|
wire B_or_data = opcode[6];
|
|
|
|
localparam S_RESET = 0;
|
|
localparam S_SELECT = 1;
|
|
localparam S_DECODE = 2;
|
|
localparam S_COMPUTE = 3;
|
|
localparam S_READ_IP = 4;
|
|
|
|
ALU alu(
|
|
.A(A),
|
|
.B(B_or_data ? data_in : B),
|
|
.Y(Y),
|
|
.aluop(aluop),
|
|
.carry(carry));
|
|
|
|
always @(posedge clk)
|
|
if (reset) begin
|
|
state <= 0;
|
|
write <= 0;
|
|
end else begin
|
|
case (state)
|
|
// state 0: reset
|
|
S_RESET: begin
|
|
IP <= 8'h80;
|
|
write <= 0;
|
|
state <= S_SELECT;
|
|
end
|
|
// state 1: select opcode address
|
|
S_SELECT: begin
|
|
address <= IP;
|
|
IP <= IP + 1;
|
|
write <= 0;
|
|
state <= S_DECODE;
|
|
end
|
|
// state 2: read/decode opcode
|
|
S_DECODE: begin
|
|
opcode <= data_in; // (only use opcode next cycle)
|
|
casez (data_in)
|
|
// ALU A + B -> dest
|
|
8'b00??????: begin
|
|
state <= S_COMPUTE;
|
|
end
|
|
// ALU A + immediate -> dest
|
|
8'b01??????: begin
|
|
address <= IP;
|
|
IP <= IP + 1;
|
|
state <= S_COMPUTE;
|
|
end
|
|
// ALU A + read [B] -> dest
|
|
8'b11??????: begin
|
|
address <= B;
|
|
state <= S_COMPUTE;
|
|
end
|
|
// A -> write [nnnn]
|
|
8'b1001????: begin
|
|
address <= {4'b0, data_in[3:0]};
|
|
data_out <= A;
|
|
write <= 1;
|
|
state <= S_SELECT;
|
|
end
|
|
// swap A,B
|
|
8'b10000001: begin
|
|
A <= B;
|
|
B <= A;
|
|
state <= S_SELECT;
|
|
end
|
|
// conditional branch
|
|
8'b1010????: begin
|
|
if (
|
|
(data_in[0] && (data_in[1] == carry)) ||
|
|
(data_in[2] && (data_in[3] == zero)))
|
|
begin
|
|
address <= IP;
|
|
state <= S_READ_IP;
|
|
end else begin
|
|
state <= S_SELECT;
|
|
end
|
|
IP <= IP + 1; // skip immediate
|
|
end
|
|
// fall-through RESET
|
|
default: begin
|
|
state <= S_RESET; // reset
|
|
end
|
|
endcase
|
|
end
|
|
// state 3: compute ALU op and flags
|
|
S_COMPUTE: begin
|
|
// transfer ALU output to destination
|
|
case (opdest)
|
|
`DEST_A: A <= Y[7:0];
|
|
`DEST_B: B <= Y[7:0];
|
|
`DEST_IP: IP <= Y[7:0];
|
|
`DEST_NOP: ;
|
|
endcase
|
|
// set carry for certain operations (4-7,12-15)
|
|
if (aluop[2]) carry <= Y[8];
|
|
// set zero flag
|
|
zero <= ~|Y[7:0];
|
|
// repeat CPU loop
|
|
state <= S_SELECT;
|
|
end
|
|
// state 4: read new IP from memory (immediate mode)
|
|
S_READ_IP: begin
|
|
IP <= data_in;
|
|
state <= S_SELECT;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
endmodule
|
|
|
|
`ifdef TOPMOD__test_CPU_top
|
|
|
|
module test_CPU_top(
|
|
input clk,
|
|
input reset,
|
|
output [7:0] address_bus,
|
|
output reg [7:0] to_cpu,
|
|
output [7:0] from_cpu,
|
|
output write_enable,
|
|
output [7:0] IP,
|
|
output [7:0] A,
|
|
output [7:0] B,
|
|
output zero,
|
|
output carry
|
|
);
|
|
|
|
reg [7:0] ram[0:127];
|
|
reg [7:0] rom[0:127];
|
|
|
|
assign IP = cpu.IP;
|
|
assign A = cpu.A;
|
|
assign B = cpu.B;
|
|
assign zero = cpu.zero;
|
|
assign carry = cpu.carry;
|
|
|
|
CPU cpu(.clk(clk),
|
|
.reset(reset),
|
|
.address(address_bus),
|
|
.data_in(to_cpu),
|
|
.data_out(from_cpu),
|
|
.write(write_enable));
|
|
|
|
always @(posedge clk)
|
|
if (write_enable) begin
|
|
ram[address_bus[6:0]] <= from_cpu;
|
|
end
|
|
|
|
always @(*)
|
|
if (address_bus[7] == 0)
|
|
to_cpu = ram[address_bus[6:0]];
|
|
else
|
|
to_cpu = rom[address_bus[6:0]];
|
|
|
|
initial begin
|
|
`ifdef EXT_INLINE_ASM
|
|
// example code: Fibonacci sequence
|
|
rom = '{
|
|
__asm
|
|
|
|
.arch femto8
|
|
.org 128
|
|
.len 128
|
|
|
|
Start:
|
|
zero A ; A <= 0
|
|
ldb #1 ; B <= 1
|
|
Loop:
|
|
add A,B ; A <= A + B
|
|
swapab ; swap A,B
|
|
bcc Loop ; repeat until carry set
|
|
reset ; end of loop; reset CPU
|
|
|
|
__endasm
|
|
};
|
|
`endif
|
|
end
|
|
|
|
endmodule
|
|
|
|
`endif
|
|
|
|
`endif
|