2018-02-15 18:31:32 +00:00
|
|
|
`ifndef CPU16_H
|
|
|
|
`define CPU16_H
|
|
|
|
|
2018-02-28 03:35:42 +00:00
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
|
|
module ALU(A, B, Y, aluop, carry);
|
|
|
|
|
|
|
|
parameter N = 8;
|
|
|
|
input [N-1:0] A;
|
|
|
|
input [N-1:0] B;
|
|
|
|
output [N:0] Y;
|
|
|
|
input [3:0] aluop;
|
|
|
|
input 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
|
2018-02-15 18:31:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
00000aaa 0++++bbb operation A+B->A
|
2018-02-19 00:19:20 +00:00
|
|
|
00001aaa 0++++bbb operation A+[B]->A
|
|
|
|
00011aaa 0++++000 operation A+imm16 -> A
|
|
|
|
00101aaa ######## load zero page
|
|
|
|
00110aaa ######## store zero page
|
|
|
|
01001aaa #####bbb load [B+#] -> A
|
2018-02-15 18:31:32 +00:00
|
|
|
01010aaa #####bbb store A -> [B+#]
|
2018-02-19 00:19:20 +00:00
|
|
|
01101aaa 0++++000 operation A+[imm16] -> A
|
|
|
|
01110aaa 00cccbbb store A -> [B+#], C -> IP
|
|
|
|
1000tttt ######## conditional branch
|
2018-02-15 18:31:32 +00:00
|
|
|
11+++aaa ######## immediate binary operation
|
|
|
|
*/
|
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
module CPU16(clk, reset, hold, busy,
|
2018-02-19 00:19:20 +00:00
|
|
|
address, data_in, data_out, write);
|
2018-02-15 18:31:32 +00:00
|
|
|
|
|
|
|
input clk;
|
|
|
|
input reset;
|
2018-02-21 19:32:11 +00:00
|
|
|
input hold;
|
2018-02-19 00:19:20 +00:00
|
|
|
output busy;
|
2018-02-15 18:31:32 +00:00
|
|
|
output [15:0] address;
|
|
|
|
input [15:0] data_in;
|
|
|
|
output [15:0] data_out;
|
|
|
|
output write;
|
|
|
|
|
2018-02-28 03:35:42 +00:00
|
|
|
// wait state for RAM?
|
|
|
|
parameter RAM_WAIT = 1;
|
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
reg [15:0] regs[0:7]; // 8 16-bit registers
|
|
|
|
reg [2:0] state; // CPU state
|
2018-02-15 18:31:32 +00:00
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
reg carry; // carry flag
|
|
|
|
reg zero; // zero flag
|
|
|
|
reg neg; // negative flag
|
2018-02-15 18:31:32 +00:00
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
wire [16:0] Y; // ALU 16-bit + carry output
|
|
|
|
reg [3:0] aluop; // ALU operation
|
|
|
|
|
|
|
|
reg [15:0] opcode; // used to decode ALU inputs
|
|
|
|
wire [2:0] rdest = opcode[10:8]; // ALU A input reg.
|
|
|
|
wire [2:0] rsrc = opcode[2:0]; // ALU B input reg.
|
|
|
|
wire Bconst = opcode[15]; // ALU B = 8-bit constant
|
|
|
|
wire Bload = opcode[11]; // ALU B = data bus
|
2018-02-15 18:31:32 +00:00
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
// CPU states
|
2018-02-15 18:31:32 +00:00
|
|
|
localparam S_RESET = 0;
|
|
|
|
localparam S_SELECT = 1;
|
|
|
|
localparam S_DECODE = 2;
|
|
|
|
localparam S_COMPUTE = 3;
|
2018-02-28 03:35:42 +00:00
|
|
|
localparam S_DECODE_WAIT = 4;
|
|
|
|
localparam S_COMPUTE_WAIT = 5;
|
2018-02-15 18:31:32 +00:00
|
|
|
|
2018-02-19 00:19:20 +00:00
|
|
|
localparam SP = 6; // stack ptr = register 6
|
2018-02-15 18:31:32 +00:00
|
|
|
localparam IP = 7; // IP = register 7
|
|
|
|
|
|
|
|
ALU #(16) alu(
|
|
|
|
.A(regs[rdest]),
|
2018-02-19 00:19:20 +00:00
|
|
|
.B(Bconst ? {8'b0, opcode[7:0]}
|
|
|
|
: Bload ? data_in
|
|
|
|
: regs[rsrc]),
|
2018-02-15 18:31:32 +00:00
|
|
|
.Y(Y),
|
|
|
|
.aluop(aluop),
|
|
|
|
.carry(carry));
|
|
|
|
|
|
|
|
always @(posedge clk)
|
|
|
|
if (reset) begin
|
2018-02-19 00:19:20 +00:00
|
|
|
state <= S_RESET;
|
|
|
|
busy <= 1;
|
2018-02-15 18:31:32 +00:00
|
|
|
end else begin
|
|
|
|
case (state)
|
|
|
|
// state 0: reset
|
|
|
|
S_RESET: begin
|
|
|
|
regs[IP] <= 16'h8000;
|
|
|
|
write <= 0;
|
|
|
|
state <= S_SELECT;
|
|
|
|
end
|
|
|
|
// state 1: select opcode address
|
|
|
|
S_SELECT: begin
|
|
|
|
write <= 0;
|
2018-02-21 19:32:11 +00:00
|
|
|
if (hold) begin
|
2018-02-19 00:19:20 +00:00
|
|
|
busy <= 1;
|
|
|
|
state <= S_SELECT;
|
|
|
|
end else begin
|
|
|
|
busy <= 0;
|
|
|
|
address <= regs[IP];
|
|
|
|
regs[IP] <= regs[IP] + 1;
|
2018-02-28 03:35:42 +00:00
|
|
|
state <= RAM_WAIT ? S_DECODE_WAIT : S_DECODE;
|
2018-02-19 00:19:20 +00:00
|
|
|
end
|
2018-02-15 18:31:32 +00:00
|
|
|
end
|
|
|
|
// state 2: read/decode opcode
|
|
|
|
S_DECODE: begin
|
2018-02-28 03:35:42 +00:00
|
|
|
// default next state
|
|
|
|
state <= RAM_WAIT && data_in[11] ? S_COMPUTE_WAIT : S_COMPUTE;
|
2018-02-15 18:31:32 +00:00
|
|
|
casez (data_in)
|
2018-02-19 00:19:20 +00:00
|
|
|
// 00000aaa0++++bbb operation A+B->A
|
2018-02-15 18:31:32 +00:00
|
|
|
16'b00000???0???????: begin
|
|
|
|
aluop <= data_in[6:3];
|
|
|
|
end
|
2018-02-19 00:19:20 +00:00
|
|
|
// 00001aaa01+++bbb operation A+[B]->A
|
|
|
|
16'b00001???01??????: begin
|
|
|
|
address <= regs[data_in[2:0]];
|
|
|
|
aluop <= data_in[6:3];
|
|
|
|
if (data_in[2:0] == SP)
|
|
|
|
regs[SP] <= regs[SP] + 1;
|
|
|
|
end
|
|
|
|
// 00011aaa0++++000 operation A+imm16 -> A
|
|
|
|
16'b00011???0????000: begin
|
|
|
|
address <= regs[IP];
|
|
|
|
regs[IP] <= regs[IP] + 1;
|
|
|
|
aluop <= data_in[6:3];
|
2018-02-15 18:31:32 +00:00
|
|
|
end
|
2018-02-19 00:19:20 +00:00
|
|
|
// 11+++aaa######## immediate binary operation
|
|
|
|
16'b11??????????????: begin
|
|
|
|
aluop <= data_in[14:11];
|
|
|
|
end
|
|
|
|
// 00101aaa######## load ZP memory
|
2018-02-15 18:31:32 +00:00
|
|
|
16'b00101???????????: begin
|
|
|
|
address <= {8'b0, data_in[7:0]};
|
|
|
|
aluop <= `OP_LOAD_B;
|
|
|
|
end
|
2018-02-19 00:19:20 +00:00
|
|
|
// 00110aaa######## store ZP memory
|
2018-02-15 18:31:32 +00:00
|
|
|
16'b00110???????????: begin
|
|
|
|
address <= {8'b0, data_in[7:0]};
|
|
|
|
data_out <= regs[data_in[10:8]];
|
|
|
|
write <= 1;
|
|
|
|
state <= S_SELECT;
|
|
|
|
end
|
2018-02-19 00:19:20 +00:00
|
|
|
// 01001aaa#####bbb [B+#] -> A
|
|
|
|
16'b01001???????????: begin
|
|
|
|
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
|
|
|
aluop <= `OP_LOAD_B;
|
|
|
|
if (data_in[2:0] == SP)
|
|
|
|
regs[SP] <= regs[SP] + 1;
|
|
|
|
end
|
|
|
|
// 01010aaa#####bbb store A -> [B+#]
|
|
|
|
16'b01010???????????: begin
|
|
|
|
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
|
|
|
data_out <= regs[data_in[10:8]];
|
|
|
|
write <= 1;
|
|
|
|
state <= S_SELECT;
|
|
|
|
if (data_in[2:0] == SP)
|
|
|
|
regs[SP] <= regs[SP] - 1;
|
|
|
|
end
|
|
|
|
// 01011aaa0++++000 operation A+[imm16] -> A
|
|
|
|
16'b01011????????000: begin
|
|
|
|
address <= regs[IP];
|
|
|
|
regs[IP] <= regs[IP] + 1;
|
|
|
|
aluop <= data_in[6:3];
|
|
|
|
end
|
2018-02-28 03:35:42 +00:00
|
|
|
// 01110aaa00cccbbb store A -> [B], C -> IP
|
2018-02-19 00:19:20 +00:00
|
|
|
16'b01110???00??????: begin
|
2018-02-28 03:35:42 +00:00
|
|
|
address <= regs[data_in[2:0]];
|
2018-02-19 00:19:20 +00:00
|
|
|
data_out <= regs[data_in[10:8]];
|
|
|
|
write <= 1;
|
|
|
|
state <= S_SELECT;
|
|
|
|
if (data_in[2:0] == SP)
|
|
|
|
regs[SP] <= regs[SP] - 1;
|
|
|
|
regs[IP] <= regs[data_in[5:3]];
|
|
|
|
end
|
|
|
|
// 1000????######## conditional branch
|
|
|
|
16'b1000????????????: begin
|
2018-02-15 18:31:32 +00:00
|
|
|
if (
|
2018-02-19 00:19:20 +00:00
|
|
|
(data_in[8] && (data_in[11] == carry)) ||
|
|
|
|
(data_in[9] && (data_in[11] == zero)) ||
|
|
|
|
(data_in[10] && (data_in[11] == neg)))
|
2018-02-15 18:31:32 +00:00
|
|
|
begin
|
|
|
|
// relative branch, sign extended
|
2018-02-19 00:19:20 +00:00
|
|
|
regs[IP] <= regs[IP] + 16'($signed(data_in[7:0]));
|
2018-02-15 18:31:32 +00:00
|
|
|
end
|
|
|
|
state <= S_SELECT;
|
|
|
|
end
|
|
|
|
// fall-through RESET
|
|
|
|
default: begin
|
|
|
|
state <= S_RESET; // reset
|
|
|
|
end
|
|
|
|
endcase
|
2018-02-21 19:32:11 +00:00
|
|
|
opcode <= data_in; // (only use opcode next cycle)
|
2018-02-15 18:31:32 +00:00
|
|
|
end
|
|
|
|
// state 3: compute ALU op and flags
|
|
|
|
S_COMPUTE: begin
|
|
|
|
// transfer ALU output to destination
|
|
|
|
regs[rdest] <= Y[15:0];
|
|
|
|
// set carry for certain operations (4-7,12-15)
|
|
|
|
if (aluop[2]) carry <= Y[16];
|
|
|
|
// set zero flag
|
|
|
|
zero <= ~|Y[15:0];
|
|
|
|
neg <= Y[15];
|
|
|
|
// repeat CPU loop
|
|
|
|
state <= S_SELECT;
|
|
|
|
end
|
2018-02-28 03:35:42 +00:00
|
|
|
// wait 1 cycle for RAM read
|
|
|
|
S_DECODE_WAIT: begin
|
|
|
|
state <= S_DECODE;
|
|
|
|
end
|
|
|
|
S_COMPUTE_WAIT : begin
|
|
|
|
state <= S_COMPUTE;
|
|
|
|
end
|
2018-02-15 18:31:32 +00:00
|
|
|
endcase
|
|
|
|
end
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
`ifdef TOPMOD__test_CPU16_top
|
|
|
|
|
2018-02-15 18:31:32 +00:00
|
|
|
module test_CPU16_top(
|
|
|
|
input clk,
|
|
|
|
input reset,
|
|
|
|
output [15:0] address_bus,
|
|
|
|
output reg [15:0] to_cpu,
|
|
|
|
output [15:0] from_cpu,
|
|
|
|
output write_enable,
|
|
|
|
output [15:0] IP,
|
|
|
|
output zero,
|
2018-02-19 00:19:20 +00:00
|
|
|
output carry,
|
2018-02-28 03:35:42 +00:00
|
|
|
output busy,
|
|
|
|
output [2:0] state
|
2018-02-15 18:31:32 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
reg [15:0] ram[0:65535];
|
2018-02-19 00:19:20 +00:00
|
|
|
reg [15:0] rom[0:255];
|
2018-02-15 18:31:32 +00:00
|
|
|
|
|
|
|
assign IP = cpu.regs[7];
|
|
|
|
assign zero = cpu.zero;
|
|
|
|
assign carry = cpu.carry;
|
2018-02-28 03:35:42 +00:00
|
|
|
assign state = cpu.state;
|
2018-02-15 18:31:32 +00:00
|
|
|
|
2018-02-19 00:19:20 +00:00
|
|
|
CPU16 cpu(
|
|
|
|
.clk(clk),
|
2018-02-15 18:31:32 +00:00
|
|
|
.reset(reset),
|
2018-02-21 19:32:11 +00:00
|
|
|
.hold(0),
|
2018-02-19 00:19:20 +00:00
|
|
|
.busy(busy),
|
2018-02-15 18:31:32 +00:00
|
|
|
.address(address_bus),
|
|
|
|
.data_in(to_cpu),
|
|
|
|
.data_out(from_cpu),
|
|
|
|
.write(write_enable));
|
|
|
|
|
|
|
|
always @(posedge clk)
|
|
|
|
if (write_enable) begin
|
|
|
|
ram[address_bus] <= from_cpu;
|
|
|
|
end
|
|
|
|
|
2018-02-28 03:35:42 +00:00
|
|
|
always @(posedge clk)
|
2018-02-15 18:31:32 +00:00
|
|
|
if (address_bus[15] == 0)
|
2018-02-28 03:35:42 +00:00
|
|
|
to_cpu <= ram[address_bus];
|
2018-02-15 18:31:32 +00:00
|
|
|
else
|
2018-02-28 03:35:42 +00:00
|
|
|
to_cpu <= rom[address_bus[7:0]];
|
2018-02-15 18:31:32 +00:00
|
|
|
|
2018-02-19 00:19:20 +00:00
|
|
|
`ifdef EXT_INLINE_ASM
|
|
|
|
initial begin
|
|
|
|
rom = '{
|
|
|
|
__asm
|
|
|
|
.arch femto16
|
|
|
|
.org 0x8000
|
|
|
|
.len 256
|
2018-02-28 03:35:42 +00:00
|
|
|
mov sp,@$6fff
|
2018-02-19 00:19:20 +00:00
|
|
|
mov dx,@Fib
|
|
|
|
jsr dx
|
|
|
|
reset
|
|
|
|
Fib:
|
|
|
|
mov ax,#1
|
|
|
|
mov bx,#0
|
|
|
|
Loop:
|
|
|
|
mov cx,ax
|
|
|
|
add ax,bx
|
|
|
|
mov bx,cx
|
|
|
|
push ax
|
|
|
|
pop ax
|
|
|
|
mov [42],ax
|
|
|
|
mov ax,[42]
|
|
|
|
bcc Loop
|
|
|
|
rts
|
|
|
|
__endasm
|
|
|
|
};
|
|
|
|
end
|
|
|
|
`endif
|
|
|
|
|
2018-02-15 18:31:32 +00:00
|
|
|
endmodule
|
|
|
|
|
|
|
|
`endif
|
2018-02-21 19:32:11 +00:00
|
|
|
|
|
|
|
`endif
|