Line 46... |
Line 46... |
//// ////
|
//// ////
|
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
|
|
`timescale 1ns / 1ps
|
`timescale 1ns / 1ps
|
|
|
module t6507lp_fsm(clk_in, rst_in_n, alu_result, alu_status, data_in, address, control, data_out, alu_opcode, alu_a, alu_enable);
|
module t6507lp_fsm(clk, reset_n, alu_result, alu_status, data_in, address, control, data_out, alu_opcode, alu_a, alu_enable);
|
|
|
parameter DATA_SIZE = 4'd8;
|
parameter DATA_SIZE = 4'd8;
|
parameter ADDR_SIZE = 4'd13;
|
parameter ADDR_SIZE = 4'd13;
|
|
|
input clk_in;
|
input clk;
|
input rst_in_n;
|
input reset_n;
|
input [DATA_SIZE-1:0] alu_result;
|
input [DATA_SIZE-1:0] alu_result;
|
input [DATA_SIZE-1:0] alu_status;
|
input [DATA_SIZE-1:0] alu_status;
|
input [DATA_SIZE-1:0] data_in;
|
input [DATA_SIZE-1:0] data_in;
|
output reg [ADDR_SIZE-1:0] address;
|
output reg [ADDR_SIZE-1:0] address;
|
output reg control; // one bit is enough? read = 0, write = 1
|
output reg control; // one bit is enough? read = 0, write = 1
|
Line 70... |
Line 69... |
localparam RESET = 4'b1111;
|
localparam RESET = 4'b1111;
|
localparam FETCH_OP = 4'b0000;
|
localparam FETCH_OP = 4'b0000;
|
localparam FETCH_OP_CALC = 4'b0001;
|
localparam FETCH_OP_CALC = 4'b0001;
|
localparam FETCH_LOW = 4'b0010;
|
localparam FETCH_LOW = 4'b0010;
|
localparam FETCH_HIGH = 4'b0011;
|
localparam FETCH_HIGH = 4'b0011;
|
|
localparam READ_MEM = 4'b0100;
|
|
localparam DUMMY_WRT_CALC = 4'b0101;
|
|
localparam WRITE_MEM = 4'b0110;
|
|
localparam FETCH_OP_CALC_PARAM = 4'b0111;
|
|
|
// OPCODES TODO: verify how this get synthesised
|
// OPCODES TODO: verify how this get synthesised
|
`include "../T6507LP_Package.v"
|
`include "../T6507LP_Package.v"
|
|
|
// control signals
|
// control signals
|
Line 81... |
Line 84... |
localparam MEM_WRITE = 1'b1;
|
localparam MEM_WRITE = 1'b1;
|
|
|
reg [ADDR_SIZE-1:0] pc; // program counter
|
reg [ADDR_SIZE-1:0] pc; // program counter
|
reg [DATA_SIZE-1:0] sp; // stack pointer
|
reg [DATA_SIZE-1:0] sp; // stack pointer
|
reg [DATA_SIZE-1:0] ir; // instruction register
|
reg [DATA_SIZE-1:0] ir; // instruction register
|
reg [ADDR_SIZE:0] temp_add; // temporary address
|
reg [ADDR_SIZE:0] temp_addr; // temporary address
|
reg [DATA_SIZE-1:0] temp_data; // temporary data
|
reg [DATA_SIZE-1:0] temp_data; // temporary data
|
|
|
reg [3:0] state, next_state; // current and next state registers
|
reg [3:0] state, next_state; // current and next state registers
|
// TODO: not sure if this will be 4 bits wide. as of march 9th this was 4bit wide.
|
// TODO: not sure if this will be 4 bits wide. as of march 9th this was 4bit wide.
|
|
|
Line 107... |
Line 110... |
reg jump;
|
reg jump;
|
|
|
wire [ADDR_SIZE-1:0] next_pc;
|
wire [ADDR_SIZE-1:0] next_pc;
|
assign next_pc = pc + 13'b0000000000001;
|
assign next_pc = pc + 13'b0000000000001;
|
|
|
always @ (posedge clk_in or negedge rst_in_n) begin // sequencial always block
|
always @ (posedge clk or negedge reset_n) begin // sequencial always block
|
if (rst_in_n == 1'b0) begin
|
if (reset_n == 1'b0) begin
|
// TODO: all internal flip-flops must assume default values
|
// all registers must assume default values
|
|
|
pc <= 0; // TODO: this is written somewhere. something about a reset vector. must be checked.
|
pc <= 0; // TODO: this is written somewhere. something about a reset vector. must be checked.
|
sp <= 0; // TODO: the default is not 0. maybe $0100 or something like that. must be checked.
|
sp <= 0; // TODO: the default is not 0. maybe $0100 or something like that. must be checked.
|
ir <= 0;
|
ir <= 0;
|
temp_add <= 0;
|
temp_addr <= 0;
|
temp_data <= 0;
|
temp_data <= 0;
|
state <= RESET;
|
state <= RESET;
|
|
// registered outputs also receive default values
|
|
address <= 0;
|
|
control <= 0; // check if these 2 shouldnt be on the other always block along with the address
|
|
data_out <= 0;
|
end
|
end
|
else begin
|
else begin
|
state <= next_state;
|
state <= next_state;
|
|
control <= MEM_READ;
|
|
|
case (state)
|
case (state)
|
RESET: begin
|
RESET: begin
|
// The processor was reset
|
// The processor was reset
|
end
|
end
|
FETCH_OP: begin // this state is the simplest one. it is a simple fetch that must be done when the cpu was reset or
|
FETCH_OP: begin // this state is the simplest one. it is a simple fetch that must be done when the cpu was reset or
|
// the last cycle was a memory write.
|
// the last cycle was a memory write.
|
pc <= next_pc;
|
pc <= next_pc;
|
|
address <= next_pc;
|
ir <= data_in;
|
ir <= data_in;
|
end
|
end
|
FETCH_OP_CALC: begin // this is the pipeline happening!
|
FETCH_OP_CALC, FETCH_OP_CALC_PARAM: begin // this is the pipeline happening!
|
pc <= next_pc;
|
pc <= next_pc;
|
|
address <= next_pc;
|
ir <= data_in;
|
ir <= data_in;
|
end
|
end
|
FETCH_LOW: begin // in this state the opcode is already known so truly execution begins
|
FETCH_LOW: begin // in this state the opcode is already known so truly execution begins
|
if (accumulator || implied) begin
|
if (accumulator || implied) begin
|
pc <= pc; // is this better?
|
pc <= pc; // is this better?
|
|
address <= pc;
|
end
|
end
|
else if (immediate) begin
|
else if (immediate) begin
|
pc <= next_pc;
|
pc <= next_pc;
|
|
address <= next_pc;
|
temp_data <= data_in; // the follow-up byte is saved in temp_data
|
temp_data <= data_in; // the follow-up byte is saved in temp_data
|
end
|
end
|
|
else if (absolute) begin
|
|
pc <= next_pc;
|
|
address <= next_pc;
|
|
temp_addr[7:0] <= data_in;
|
end
|
end
|
default: begin
|
|
$write("unknown state"); // TODO: check if synth really ignores this 2 lines. Otherwise wrap it with a `ifdef
|
|
$finish(0);
|
|
end
|
end
|
|
FETCH_HIGH: begin
|
endcase
|
if (jump) begin
|
|
pc <= {data_in[4:0], temp_addr}; // PCL <= first byte, PCH <= second byte
|
|
address <= {data_in[4:0], temp_addr};
|
end
|
end
|
|
else begin
|
|
if (write) begin
|
|
pc <= next_pc;
|
|
temp_addr[12:8] <= data_in[4:0];
|
|
address <= {data_in[4:0],temp_addr[7:0]};
|
|
control <= MEM_WRITE;
|
end
|
end
|
|
else begin // read_modify_write or just read
|
always @ (posedge clk_in or negedge rst_in_n) begin
|
pc <= next_pc;
|
if (rst_in_n == 1'b0) begin
|
temp_addr[12:8] <= data_in[4:0];
|
// TODO: all outputs must assume default values
|
address <= {data_in[4:0],temp_addr[7:0]};
|
address <= 0;
|
|
control <= 0; // one bit is enough? read = 0, write = 1
|
|
data_out <= 0;
|
|
alu_opcode <= 0;
|
|
alu_a <= 0;
|
|
alu_enable <= 0;
|
|
end
|
end
|
else begin
|
|
|
|
address <= pc;
|
|
|
|
case (state)
|
|
RESET: begin
|
|
// The processor was reset. No output whatsoever.
|
|
end
|
end
|
FETCH_OP: begin
|
//else begin
|
// it is a simple fetch. no output change.
|
// $write("FETCHHIGH PROBLEM");
|
|
// $finish(0);
|
|
//end
|
|
end
|
|
READ_MEM: begin
|
|
if (read_modify_write) begin
|
|
pc <= pc;
|
|
address <= temp_addr;
|
|
control <= MEM_WRITE;
|
|
temp_data <= data_in;
|
|
data_out <= data_in; // writeback the same value
|
end
|
end
|
FETCH_OP_CALC: begin // this is the pipeline happening!
|
else begin
|
alu_opcode <= ir;
|
pc <= pc;
|
alu_a <= temp_data;
|
address <= pc;
|
alu_enable <= 1'b1;
|
temp_data <= data_in;
|
end
|
end
|
FETCH_LOW: begin // in this state the opcode is already known so truly execution begins
|
|
if (accumulator || implied) begin
|
|
alu_opcode <= ir;
|
|
alu_enable <= 1'b1;
|
|
end
|
end
|
else begin // nothing?
|
DUMMY_WRT_CALC: begin
|
|
pc <= pc;
|
|
address <= temp_addr;
|
|
control <= MEM_WRITE;
|
|
data_out <= alu_result;
|
end
|
end
|
|
WRITE_MEM: begin
|
|
pc <= pc;
|
|
address <= pc;
|
|
data_out = 8'hzz;
|
end
|
end
|
default: begin
|
default: begin
|
$write("unknown state"); // TODO: check if synth really ignores this 2 lines. Otherwise wrap it with a `ifdef
|
$write("unknown state"); // TODO: check if synth really ignores this 2 lines. Otherwise wrap it with a `ifdef
|
$finish(0);
|
$finish(0);
|
end
|
end
|
|
|
endcase
|
endcase
|
end
|
end
|
|
|
end
|
end
|
|
|
always @ (*) begin // this is the next_state logic always block
|
always @ (*) begin // this is the next_state logic and the output logic always block
|
//address = pc;
|
|
//control = MEM_READ;
|
//control = MEM_READ;
|
//data_out = 8'h00;
|
//data_out = 8'h00;
|
//alu_opcode = 8'h00;
|
|
//alu_a = 8'h00;
|
alu_opcode = 8'h00;
|
//alu_enable = 1'b0;
|
alu_a = 8'h00;
|
|
alu_enable = 1'b0;
|
|
//address = pc;
|
|
|
next_state = RESET; // this prevents the latch
|
next_state = RESET; // this prevents the latch
|
|
|
begin
|
|
case (state)
|
case (state)
|
RESET: begin
|
RESET: begin
|
next_state = FETCH_OP;
|
next_state = FETCH_OP;
|
end
|
end
|
FETCH_OP: begin
|
FETCH_OP: begin
|
next_state = FETCH_LOW;
|
next_state = FETCH_LOW;
|
|
//address = pc;
|
end
|
end
|
FETCH_OP_CALC: begin
|
FETCH_OP_CALC: begin
|
next_state = FETCH_LOW;
|
next_state = FETCH_LOW;
|
//alu_opcode = ir;
|
alu_opcode = ir;
|
//alu_enable = 1'b1;
|
alu_enable = 1'b1;
|
|
//address = next_pc;
|
|
|
|
end
|
|
FETCH_OP_CALC_PARAM: begin
|
|
next_state = FETCH_LOW;
|
|
alu_opcode = ir;
|
|
alu_enable = 1'b1;
|
|
alu_a = temp_data;
|
|
//address = next_pc;
|
end
|
end
|
FETCH_LOW: begin
|
FETCH_LOW: begin
|
if (accumulator || implied) begin
|
if (accumulator || implied) begin
|
//alu_opcode = data_in;
|
alu_opcode = ir;
|
//alu_enable = 1'b1;
|
alu_enable = 1'b1;
|
next_state = FETCH_OP;
|
next_state = FETCH_OP;
|
end
|
end
|
else if (immediate) begin
|
else if (immediate) begin
|
next_state = FETCH_OP_CALC;
|
next_state = FETCH_OP_CALC_PARAM;
|
end
|
end
|
else begin // at least the immediate address mode falls here
|
else begin // at least the absolute address mode falls here
|
next_state = FETCH_HIGH;
|
next_state = FETCH_HIGH;
|
end
|
end
|
|
//address = next_pc;
|
|
|
|
end
|
|
FETCH_HIGH: begin
|
|
if (jump) begin
|
|
next_state = FETCH_OP;
|
|
end
|
|
else if (read || read_modify_write) begin
|
|
next_state = READ_MEM;
|
|
end
|
|
else if (write) begin
|
|
next_state = WRITE_MEM;
|
|
end
|
|
else begin
|
|
$write("unknown behavior");
|
|
$finish(0);
|
|
end
|
|
end
|
|
READ_MEM: begin
|
|
if (read) begin
|
|
next_state = FETCH_OP_CALC_PARAM;
|
|
end
|
|
else if (read_modify_write) begin
|
|
next_state = DUMMY_WRT_CALC;
|
|
end
|
|
end
|
|
DUMMY_WRT_CALC: begin
|
|
alu_opcode = ir;
|
|
alu_enable = 1'b1;
|
|
alu_a = data_in;
|
|
next_state = WRITE_MEM;
|
|
end
|
|
WRITE_MEM: begin
|
|
next_state = FETCH_OP;
|
end
|
end
|
default: begin
|
default: begin
|
next_state = RESET;
|
next_state = RESET;
|
end
|
end
|
endcase
|
endcase
|
end
|
end
|
end
|
|
|
|
// this always block is responsible for updating the address mode
|
// this always block is responsible for updating the address mode
|
always @ (*) begin //
|
always @ (*) begin //
|
absolute = 1'b0;
|
absolute = 1'b0;
|
absolute_indexed = 1'b0;
|
absolute_indexed = 1'b0;
|
Line 291... |
Line 353... |
end
|
end
|
ADC_IDX, AND_IDX, CMP_IDX, EOR_IDX, LDA_IDX, ORA_IDX, SBC_IDX, STA_IDX, ADC_IDY, AND_IDY, CMP_IDY, EOR_IDY, LDA_IDY,
|
ADC_IDX, AND_IDX, CMP_IDX, EOR_IDX, LDA_IDX, ORA_IDX, SBC_IDX, STA_IDX, ADC_IDY, AND_IDY, CMP_IDY, EOR_IDY, LDA_IDY,
|
ORA_IDY, SBC_IDY, STA_IDY: begin // all these opcodes are 8'hX1; TODO: optimize this
|
ORA_IDY, SBC_IDY, STA_IDY: begin // all these opcodes are 8'hX1; TODO: optimize this
|
indirect = 1'b1;
|
indirect = 1'b1;
|
end
|
end
|
|
default: begin
|
|
$write("unknown OPCODE!");
|
|
$finish();
|
|
end
|
endcase
|
endcase
|
|
|
if (data_in == JMP_ABS || data_in == JMP_IND) begin // the opcodes are 8'h4C and 8'h6C
|
case (ir)
|
jump = 1'b1;
|
ASL_ACC, ASL_ZPG, ASL_ZPX, ASL_ABS, ASL_ABX, LSR_ACC, LSR_ZPG, LSR_ZPX, LSR_ABS, LSR_ABX, ROL_ACC, ROL_ZPG, ROL_ZPX, ROL_ABS,
|
|
ROL_ABX, ROR_ACC, ROR_ZPG, ROR_ZPX, ROR_ABS, ROR_ABX, INC_ZPG, INC_ZPX, INC_ABS, INC_ABX, DEC_ZPG, DEC_ZPX, DEC_ABS,
|
|
DEC_ABX: begin
|
|
read_modify_write = 1'b1;
|
end
|
end
|
|
STA_ZPG, STA_ZPX, STA_ABS, STA_ABX, STA_ABY, STA_IDX, STA_IDY, STX_ZPG, STX_ZPY, STX_ABS, STY_ZPG, STY_ZPX, STY_ABS: begin
|
|
write = 1'b1;
|
|
end
|
|
default: begin // this should work fine since the previous case statement will detect the unknown/undocumented/unsupported opcodes
|
|
read = 1'b1;
|
|
end
|
|
endcase
|
|
|
// if (data_in == )
|
if (ir == JMP_ABS || ir == JMP_IND) begin // the opcodes are 8'h4C and 8'h6C
|
|
jump = 1'b1;
|
/*LDA_IMM = 8'hA9,
|
end
|
LDA_ZPG = 8'hA5,
|
|
LDA_ZPX = 8'hB5,
|
|
LDA_ABS = 8'hAD,
|
|
LDA_ABX = 8'hBD,
|
|
LDA_ABY = 8'hB9,
|
|
LDA_IDX = 8'hA1,
|
|
LDA_IDY = 8'hB1;
|
|
LDX_IMM = 8'hA2,
|
|
LDX_ZPG = 8'hA6,
|
|
LDX_ZPY = 8'hB6,
|
|
LDX_ABS = 8'hAE,
|
|
LDX_ABY = 8'hBE;
|
|
LDY_IMM = 8'hA0,
|
|
LDY_ZPG = 8'hA4,
|
|
LDY_ZPX = 8'hB4,
|
|
LDY_ABS = 8'hAC,
|
|
LDY_ABX = 8'hBC;
|
|
*/
|
|
|
|
end // no way
|
end // no way
|
endmodule
|
endmodule
|
|
|
|
|
|
|