/* The MIT License (MIT) Copyright (c) 2017 Ruchira Hasaranga Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // R8CPU (C) 2017 Hasaranga // Uses behaviour modeling. // 11 bit address bus // 8 bit databus // CISC machine // boots from address 001 00000000 // ax = 8bit register // instructions format: mov dst,src // addr=11bits, value=8bits // reading: mov1 ax, addr (total 3 bytes) // writing: mov2 addr, ax (total 3 bytes) // mov3 addr, value (total 4 bytes) // flow cont: jmp1 addr (total 3 bytes) // je addr (total 3 bytes) // jne addr (total 3 bytes) // jl addr (total 3 bytes) ( jump if value is less than ax) // jle addr (total 3 bytes) ( jump if value is less than or equal to ax) // jg addr (total 3 bytes) ( jump if value is greater than ax) // jge addr (total 3 bytes) ( jump if value is greater than or equal to ax) // arithmetic: inc ax (total 1 byte) // cmp ax, value (total 2 bytes) (compare value with ax) /* Writing to device: put value into addrbus put value into databus set en HIGH then wait for 2 cycles set wr HIGH for 2 cycles set wr LOW for 2 cycles wait till busy LOW set en LOW for 2 cycles (optional) Reading from device: put value into addrbus set en HIGH then wait for 2 cycles set rd HIGH for 2 cycles set rd LOW for 2 cycles wait till busy LOW set oe HIGH for 1 cycle databus is valid now (still oe HIGH) set oe LOW for 1 cycle set en LOW for 2 cycles (optional) device oe response must be instant (use combinational logic). */ module R8CPU ( input clk, input reset, input hlt, input deviceBusy, inout [7:0] databus, output reg [10:0] addressbus, output reg wr, output reg rd, output reg oe, output reg hlt_indicator ); reg databusIsOutput=1'b0; reg [7:0] r_databus; assign databus = databusIsOutput ? r_databus : 8'bZZZZZZZZ; reg [7:0] ax; reg [10:0] pc; reg [7:0] opcode; reg [7:0] arg1; reg [7:0] arg2; reg [7:0] arg3; reg ef,gtf,ltf; // ef= equal flag, gtf= greater than flag, ltf= less than flag parameter s_RESET = 8'd0; // power on, or reset btn press parameter s_FETCH_OPCODE = 8'd1; parameter s_READ_DATA = 8'd2; parameter s_READ_DATA_ENABLE_RD = 8'd3; parameter s_READ_DATA_DISABLE_RD = 8'd4; parameter s_READ_DATA_BUSY = 8'd5; parameter s_READ_DATA_ACQUIRE_DATA = 8'd6; parameter s_READ_DATA_DONE = 8'd7; parameter s_WAIT = 8'd8; parameter s_FETCH_OPCODE_DONE = 8'd9; parameter s_DECODE_OPCODE = 8'd10; parameter s_EXEC_MOV1 = 8'd11; parameter s_EXEC_MOV1_DONE = 8'd12; parameter s_FETCH_2ARGS = 8'd13; parameter s_FETCH_2ARGS_1DONE = 8'd14; parameter s_FETCH_2ARGS_2DONE = 8'd15; parameter s_CPU_HLT = 8'd16; parameter s_EXEC_MOV2 = 8'd17; parameter s_EXEC_MOV2_DONE = 8'd18; parameter s_WRITE_DATA = 8'd19; parameter s_WRITE_DATA_ENABLE_WR = 8'd20; parameter s_WRITE_DATA_DISABLE_WR = 8'd21; parameter s_WRITE_DATA_BUSY = 8'd22; parameter s_EXEC_MOV3 = 8'd23; parameter s_EXEC_MOV3_DONE = 8'd24; parameter s_FETCH_3ARGS = 8'd25; parameter s_FETCH_3ARGS_1DONE = 8'd26; parameter s_FETCH_3ARGS_2DONE = 8'd27; parameter s_FETCH_3ARGS_3DONE = 8'd28; parameter s_EXEC_JMP1 = 8'd29; parameter s_EXEC_JMP1_DONE = 8'd30; parameter s_EXEC_CMPAX = 8'd31; parameter s_EXEC_JE = 8'd32; parameter s_EXEC_JNE = 8'd33; parameter s_EXEC_JL = 8'd34; parameter s_EXEC_JLE = 8'd35; parameter s_EXEC_JG = 8'd36; parameter s_EXEC_JGE = 8'd37; parameter op_MOV1 =8'd1; // do not use 8'd0. it is used to detect non responding device! parameter op_MOV2 =8'd2; parameter op_MOV3 =8'd3; parameter op_JMP1 =8'd4; parameter op_INCAX =8'd5; parameter op_CMPAX =8'd6; parameter op_JE =8'd7; parameter op_JNE =8'd8; parameter op_JL =8'd9; parameter op_JLE =8'd10; parameter op_JG =8'd11; parameter op_JGE =8'd12; reg [7:0] SM_Main = s_RESET; reg [3:0] SM_Wait_Repeat_Index = 4'd1; reg [3:0] SM_Wait_Cycles; reg [7:0] SM_After_Wait; reg [7:0] SM_After_Read; reg [7:0] SM_After_Args_Read; reg [7:0] SM_After_Write; always @ ( posedge clk) begin if(reset == 1'b1) // reset btn pressed begin SM_Main <= s_RESET; end else if(hlt == 1'b1) begin SM_Main <= s_CPU_HLT; end else begin case (SM_Main) s_RESET: begin databusIsOutput <= 1'b0; r_databus <= 8'd0; addressbus <= 11'd0; pc <= 11'b00100000000; ax <= 8'd0; wr <= 1'b0; rd <= 1'b0; oe <= 1'b0; ef <= 1'b0; gtf <= 1'b0; ltf <= 1'b0; hlt_indicator <= 1'b0; SM_Wait_Repeat_Index <= 4'd1; SM_Main <= s_FETCH_OPCODE; end s_FETCH_OPCODE: // put pc value into addressbus, read from that address begin addressbus <= pc; SM_After_Read <= s_FETCH_OPCODE_DONE; SM_Main <= s_READ_DATA; end s_FETCH_OPCODE_DONE: begin opcode <= r_databus; SM_Main <= s_DECODE_OPCODE; end s_DECODE_OPCODE: begin case (opcode) op_MOV1: // mov1 ax, addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_MOV1; SM_Main <= s_FETCH_2ARGS; end op_MOV2: // mov2 addr,ax (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_MOV2; SM_Main <= s_FETCH_2ARGS; end op_MOV3: // mov3 addr, value (read next 3 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_MOV3; SM_Main <= s_FETCH_3ARGS; end op_JMP1: // jmp1 addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_JMP1; SM_Main <= s_FETCH_2ARGS; end op_INCAX: begin ax <= ax + 8'd1; pc <= pc + 11'd1; // increase pc by 1 SM_Main <= s_FETCH_OPCODE; end op_CMPAX: // read next 1 byte begin addressbus <= addressbus + 11'd1; SM_After_Read <= s_EXEC_CMPAX; SM_Main <= s_READ_DATA; end op_JE: // je addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_JE; SM_Main <= s_FETCH_2ARGS; end op_JNE: // jne addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_JNE; SM_Main <= s_FETCH_2ARGS; end op_JL: // jl addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_JL; SM_Main <= s_FETCH_2ARGS; end op_JLE: // jle addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_JLE; SM_Main <= s_FETCH_2ARGS; end op_JG: // jg addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_JG; SM_Main <= s_FETCH_2ARGS; end op_JGE: // jge addr (read next 2 bytes) begin addressbus <= addressbus + 11'd1; SM_After_Args_Read <= s_EXEC_JGE; SM_Main <= s_FETCH_2ARGS; end default: // invalid opcode begin SM_Main <= s_CPU_HLT; end endcase end //======================================================================== s_EXEC_MOV1: begin addressbus <= {arg1[2:0], arg2}; SM_After_Read <= s_EXEC_MOV1_DONE; SM_Main <= s_READ_DATA; end s_EXEC_MOV1_DONE: begin ax <= r_databus; pc <= pc + 11'd3; // increase pc by 3 bytes SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_MOV2: begin addressbus <= {arg1[2:0], arg2}; r_databus <= ax; SM_After_Write <= s_EXEC_MOV2_DONE; SM_Main <= s_WRITE_DATA; end s_EXEC_MOV2_DONE: begin pc <= pc + 11'd3; // increase pc by 3 bytes SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_MOV3: begin addressbus <= {arg1[2:0], arg2}; r_databus <= arg3; SM_After_Write <= s_EXEC_MOV3_DONE; SM_Main <= s_WRITE_DATA; end s_EXEC_MOV3_DONE: begin pc <= pc + 11'd4; // increase pc by 4 bytes SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_JMP1: begin pc <= {arg1[2:0], arg2}; SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_CMPAX: begin if(r_databus == ax) ef <= 1'b1; else ef <= 1'b0; if(r_databus < ax) ltf <= 1'b1; else ltf <= 1'b0; if(r_databus > ax) gtf <= 1'b1; else gtf <= 1'b0; pc <= pc + 11'd2; // increase pc by 2 bytes SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_JE: begin if(ef == 1'b1) pc <= {arg1[2:0], arg2}; else pc <= pc + 11'd3; SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_JNE: begin if(ef == 1'b0) pc <= {arg1[2:0], arg2}; else pc <= pc + 11'd3; SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_JL: begin if(ltf == 1'b1) pc <= {arg1[2:0], arg2}; else pc <= pc + 11'd3; SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_JLE: begin if( (ltf == 1'b1) | (ef == 1'b1) ) pc <= {arg1[2:0], arg2}; else pc <= pc + 11'd3; SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_JG: begin if(gtf == 1'b1) pc <= {arg1[2:0], arg2}; else pc <= pc + 11'd3; SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_EXEC_JGE: begin if( (gtf == 1'b1) | (ef == 1'b1) ) pc <= {arg1[2:0], arg2}; else pc <= pc + 11'd3; SM_Main <= s_FETCH_OPCODE; end //======================================================================== s_FETCH_2ARGS: // set addressbus, SM_After_Args_Read, then call this. results will be in arg1 & arg2 begin SM_After_Read <= s_FETCH_2ARGS_1DONE; SM_Main <= s_READ_DATA; end s_FETCH_2ARGS_1DONE: begin arg1 <= r_databus; addressbus <= addressbus + 11'd1; SM_After_Read <= s_FETCH_2ARGS_2DONE; SM_Main <= s_READ_DATA; end s_FETCH_2ARGS_2DONE: begin arg2 <= r_databus; SM_Main <= SM_After_Args_Read; end //======================================================================== s_FETCH_3ARGS: // set addressbus, SM_After_Args_Read, then call this. results will be in arg1, arg2 & arg3 begin SM_After_Read <= s_FETCH_3ARGS_1DONE; SM_Main <= s_READ_DATA; end s_FETCH_3ARGS_1DONE: begin arg1 <= r_databus; addressbus <= addressbus + 11'd1; SM_After_Read <= s_FETCH_3ARGS_2DONE; SM_Main <= s_READ_DATA; end s_FETCH_3ARGS_2DONE: begin arg2 <= r_databus; addressbus <= addressbus + 11'd1; SM_After_Read <= s_FETCH_3ARGS_3DONE; SM_Main <= s_READ_DATA; end s_FETCH_3ARGS_3DONE: begin arg3 <= r_databus; SM_Main <= SM_After_Args_Read; end //======================================================================== s_CPU_HLT: begin hlt_indicator <= 1'b1; addressbus <= 11'd0; databusIsOutput <= 1'b0; wr <= 1'b0; rd <= 1'b0; oe <= 1'b0; SM_Main <= s_CPU_HLT; end //======================================================================== s_WRITE_DATA: // put value into addressbus, r_databus & SM_After_Write then call this begin databusIsOutput <= 1'b1; SM_Wait_Cycles <= 4'd1; SM_After_Wait <= s_WRITE_DATA_ENABLE_WR; SM_Main <= s_WAIT; end s_WRITE_DATA_ENABLE_WR: begin wr <= 1'b1; SM_Wait_Cycles <= 4'd1; SM_After_Wait <= s_WRITE_DATA_DISABLE_WR; SM_Main <= s_WAIT; end s_WRITE_DATA_DISABLE_WR: begin wr <= 1'b0; SM_Wait_Cycles <= 4'd1; SM_After_Wait <= s_WRITE_DATA_BUSY; SM_Main <= s_WAIT; end s_WRITE_DATA_BUSY: begin if(deviceBusy == 1'b0) begin databusIsOutput <= 1'b0; SM_Main <= SM_After_Write; end else begin SM_Main <= s_WRITE_DATA_BUSY; end end //======================================================================== s_READ_DATA: // put value into addressbus, set SM_After_Read, then call this. result will be in r_databus. begin SM_Wait_Cycles <= 4'd1; SM_After_Wait <= s_READ_DATA_ENABLE_RD; SM_Main <= s_WAIT; end s_READ_DATA_ENABLE_RD: begin rd <= 1'b1; SM_Wait_Cycles <= 4'd1; SM_After_Wait <= s_READ_DATA_DISABLE_RD; SM_Main <= s_WAIT; end s_READ_DATA_DISABLE_RD: begin rd <= 1'b0; SM_Wait_Cycles <= 4'd1; SM_After_Wait <= s_READ_DATA_BUSY; SM_Main <= s_WAIT; end s_READ_DATA_BUSY: begin if(deviceBusy == 1'b0) begin oe <= 1'b1; SM_Main <= s_READ_DATA_ACQUIRE_DATA; end else begin SM_Main <= s_READ_DATA_BUSY; end end s_READ_DATA_ACQUIRE_DATA: begin r_databus <= databus; SM_Main <= s_READ_DATA_DONE; end s_READ_DATA_DONE: begin oe <= 1'b0; SM_Main <= SM_After_Read; end //======================================================================== s_WAIT: // set SM_Wait_Cycles and SM_After_Wait then call this to wait given clock cycles begin if(SM_Wait_Repeat_Index == SM_Wait_Cycles) begin SM_Wait_Repeat_Index <= 4'd1; SM_Main <= SM_After_Wait; end else begin SM_Wait_Repeat_Index <= SM_Wait_Repeat_Index + 4'd1; SM_Main <= s_WAIT; end end //======================================================================== default: SM_Main <= s_RESET; endcase end end endmodule