diff --git a/projects/popcorn/src/Pc_spec.PDF b/projects/popcorn/src/Pc_spec.PDF new file mode 100644 index 0000000..bb9ab9d Binary files /dev/null and b/projects/popcorn/src/Pc_spec.PDF differ diff --git a/projects/popcorn/src/inc.h b/projects/popcorn/src/inc.h new file mode 100644 index 0000000..db997db --- /dev/null +++ b/projects/popcorn/src/inc.h @@ -0,0 +1,25 @@ + +// +// CLOCK DIVIDER +// +// UNcomment the below to use a +// clock divider. This will allow a much +// faster external clock to be used and still +// slow down the internal core frequency. +// USE the paramter N in PC.V to adjust the +// frequency. Minimum N is 3. +// COMMNET the below to use the external clock +// directly as is. Note that the core will +// always run at 1/4 the external clock. +// +//`define use_clock_divider + + +// +// DEBUG SIGNALS +// +// UNcomment the below to activate the +// test bus for observability during debug +// +// `define show_debug_pins + diff --git a/projects/popcorn/src/pc.v b/projects/popcorn/src/pc.v new file mode 100644 index 0000000..d7dc25d --- /dev/null +++ b/projects/popcorn/src/pc.v @@ -0,0 +1,335 @@ +/* PC.V +** +** This core adheres to GNU Public Licence +** Jeung Joon Lee *** www.cmosexod.com +** joon.lee@quantum.com +** +** JJL 12/12/98 +** updated 3/16/2000 +** +** Highest module for the 8 bit general purpose microprocessor +** Two submodules are declerared here, popcorn.v and sequencer.v +** The role and functions of these modules are as following: +** PC.V wrapper, and clock generator. Provides to the submodules +** sys_clk and phase_1_clk +** POPCORN.V The 8 bit datapath. Contains the ALU, register file, +** the program counter, stack pointer and all of the muxes. +** SEQUENCER.V The sequencer state machine. BAsed on the fetched opcode, +** generates all of the sequence of control signals to +** fetch more operands or finish executing the opcode. +** +*/ + +`include "inc.h" + +module pc( sys_clk, + sys_rst, + addx_bus, + data_bus, + code_cs_l, + code_rd_l, + code_wr_l, + sram_cs_l, + slow_clk, + port + +`ifdef show_debug_pins + , + w_opl, + w_pc, + w_oplo, + state, + w_acc, + w_ax, + w_bx, + w_p, + w_ophi, + w_flag, + phase_1_clk, + slow_clk4, + reg_flag, + tst_bus, + reg_acc, + reg_ax, + reg_bx, + reg_p, + reg_opl +`endif + +); + + +parameter N=6; + + +input sys_clk; +input sys_rst; +inout [7:0] data_bus; +inout [7:0] port; +//input [1:0] tst_bus_sel; +output [11:0] addx_bus; +//output [7:0] tst_bus; +output code_cs_l, code_rd_l, code_wr_l, sram_cs_l; +output slow_clk; + +`ifdef show_debug_pins +// DEBUG SIGNALS +output w_opl; +output w_pc; +output w_oplo; +output [3:0] state; +output w_acc; +output w_ax; +output w_bx; +output w_p; +output w_ophi; +output w_flag; +output phase_1_clk; +output slow_clk4; +output [2:0] reg_flag; +output tst_bus; +output [7:0] reg_acc; +output [7:0] reg_ax; +output [7:0] reg_bx; +output [7:0] reg_p; +output [7:0] reg_opl; +`endif + +wire w_acc,w_ax,w_bx,w_p,w_flag; +wire pc_mux,w_pc,w_oplo,w_ophi,w_opl; +wire slow_clk4; +wire [2:0] bbus_mux; +wire [3:0] alu_func; +wire [2:0] reg_flag; +wire [7:0] port; +reg [1:0] state_slow_clk4; +reg phase_1_clk; +wire xx,slow_clk; +wire [5:0] state; +wire [7:0] b_bus,c_bus; +wire [11:0] d_bus; +wire [7:0] tst_bus; +wire sp_mux, w_sp; +wire [1:0] addx_mux; +wire data_bus_wr; +wire sram_cs_l, code_wr_l; +wire flag_mux; +wire [7:0] reg_acc; +wire [7:0] reg_ax; +wire [7:0] reg_bx; +wire [7:0] reg_p; +wire [7:0] reg_opl; +//wire [7:0] port_in; +//wire port_bus_ena; + +// define test bus +/* +always @(tst_bus_sel or reg_opl or c_bus or b_bus or d_bus) + case (tst_bus_sel) + 2'b00: tst_bus <= reg_opl; + //2'b01: tst_bus <= b_bus; + default: tst_bus <= b_bus; + //2'b10: tst_bus <= c_bus; + //default: tst_bus <= c_bus; + //2'b11: tst_bus <= d_bus; + endcase +*/ +//assign tst_bus=tst_bus_sel[0] ? b_bus : c_bus; +assign tst_bus=c_bus; + + +`ifdef use_clock_divider +// +// CLOCK DIVIDER +// +// +// N=5 sets for 1Mhz operation +// N=7 sets for 250Khz op. good for slow speed debug +reg [N-1:0] clk; +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) clk <=0; + else clk <= clk + 1; +assign slow_clk = clk[N-1]; +assign slow_clk4 = ~clk[N-3]; +`else +reg [1:0] clk; +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) clk <= 2'b00; + else clk <= clk + 1; +assign slow_clk = clk[1]; +assign slow_clk4 = sys_clk; +`endif + +/* define phase_1 clock +** | | +** | ___ ___ ___ ___ | ___ ___ +** slow_clk*4 |/ \___/ \___/ \___/ \___|/ \___/ \___ +** | _______ _______ | _______ +** slow_clk*2 |/ \_______/ \_______|/ \_______ +** | _______________ | _______________ +** slow_clk |/ \_______________|/ +** |________ _______|_ +** phase_1 | \_______________/ | +** | | +*/ +always @(posedge slow_clk4 or negedge sys_rst) + if (~sys_rst) begin + state_slow_clk4<=2'b00; + phase_1_clk<=1'b1; + end + else case (state_slow_clk4) + 2'b00: begin state_slow_clk4<=2'b01; phase_1_clk<=1'b1; end + 2'b01: begin state_slow_clk4<=2'b11; phase_1_clk<=1'b1; end + 2'b11: begin state_slow_clk4<=2'b10; phase_1_clk<=1'b0; end + 2'b10: begin state_slow_clk4<=2'b00; phase_1_clk<=1'b0; end + endcase + +// instantiate the datapath +popcorn POPCORN( .sys_clk(slow_clk), + .sys_rst(sys_rst), + .w_acc(w_acc), + .w_ax(w_ax), + .w_bx(w_bx), + .w_p(w_p), + .w_flag(w_flag), + .bbus_mux(bbus_mux), + .alu_func(alu_func), + .pc_mux(pc_mux), + .sp_mux(sp_mux), + .flag_mux(flag_mux), + .addx_mux(addx_mux), + .w_pc(w_pc), + .w_sp(w_sp), + .w_oplo(w_oplo), + .w_ophi(w_ophi), + .w_opl(w_opl), + .data_bus(data_bus), + .port(port), +// .port_in(port_in), + .addx_bus(addx_bus), + .reg_opl(reg_opl), + .reg_flag(reg_flag), + .b_bus(b_bus), + .c_bus(c_bus), + .d_bus(d_bus), + .data_bus_wr(data_bus_wr), + .code_wr_l(code_wr_l), + .reg_acc(reg_acc), + .reg_ax(reg_ax), + .reg_bx(reg_bx), + .reg_p(reg_p) + ); + +// instantiate the sequencer +sequencer SEQ ( .sys_clk(slow_clk), + .phase_1_clk(phase_1_clk), + .sys_rst(sys_rst), + .w_acc(w_acc), + .w_ax(w_ax), + .w_bx(w_bx), + .w_p(w_p), + .w_flag(w_flag), + .bbus_mux(bbus_mux), + .alu_func(alu_func), + .pc_mux(pc_mux), + .addx_mux(addx_mux), + .sp_mux(sp_mux), + .flag_mux(flag_mux), + .w_pc(w_pc), + .w_sp(w_sp), + .w_oplo(w_oplo), + .w_ophi(w_ophi), + .w_opl(w_opl), + .reg_opl(reg_opl), + .code_cs_l(code_cs_l), + .code_rd_l(code_rd_l), + .code_wr_l(code_wr_l), + .sram_cs_l(sram_cs_l), + .reg_flag(reg_flag), + .data_bus_wr(data_bus_wr), + .next_state(state) + ); + + + +endmodule + + + +/* +** Note on pin assignment: +** o use pin file : "finalpin.pin" +** o use global clock Y0 for 66Mhz source +** o use global reset for reset. +** o use N=6. This requires 64Mhz to give internal clock speed of 1Mhz. +** +** 2/12/99 +** o Fixed the LDM ACC,xx bug. ALU was not being set for c_bus = b_bus. +** Call this 'popcorn - final' +** +** 2/11/99 +** o Uncovered and fixed 2 bugs relating the IO PORT: +** 1.The signals 'reg_port_gate' which controls the gate of the port +** output buffer has the wrong polarity. This was causing the port +** to drive the port IO pin only when it was being read. +** 2.The state of the bbus_mux[2:0] during LDACC/STADD was set to unknown. +** This was causing the reg_port_gate to be deasserted during LDACC PORT +** or STACC PORT instructions for 1 cycle, causing glitches in the IO port +** pin +** +** 1/8/99 +** o Succesfully ran a timer program which toggles all bits of the +** port register at 2mS interval (period=4mS). The program uses +** an instrunctions timed 2mS delay subroutine. +** +** 1/7/99 +** o Somewhat final version. The test bus (TST[7:0]) has been dropped +** and PORT[7:0] been used. All instructions works at least at 2 Mhz. +** Call this 'popcorn -v5' +** +** 1/5/99 +** o Droppped the automatic pushing and popping of the flags when CALL/RET +** are executed, (could not fit). So reduced the size of REG_OPHI down +** to 4 from 8 bits. Instead the pushing of the flag is now done with +** LDACC flag +** LDM flag, #addx +** PUSH flag +** POP flag +** instructions. The flag register still is muxed from ALU and c_bus. +** Thanks Lord. +** All that remains now is implementation of interrupt support. +** +** 1/3/99 +** o Removed DX and CX register from the register file, to free-up +** the b_mux. The freed b_bus mux inputs are used by the PC (hi and low). +** This allows PC to be pushed into the stack as well as CALL and RET +** and push flag instructions +** +** 1/1/99 +** o Removed 7 level-deep hardware stack - it was obvious that this is not +** a very robust way of having a stack. It limits the CALL/RET depth. +** However my initial rational for not implementing the SP was that I +** did not wanted to have a SRAM for stack. SRAM automatically implies +** an extenal IC (since on FPGA you could not provide the required +** amount of space). +** o Added another addressing mode: direct mode, and 2 instructions: LDM and +** STM (load mem, store mem). This brings the modes +** to: register direct, immediate and direct. +** Why not, I had decided to have exnal SRAM, so let's make use of it +** +** 12/28/98 +** o Added 7-level deep hardware LIFO to implement pushacc/popacc +** instructions +** o Implemented pushacc and popacc instructions in the sequencer.v +** +** 12/27/98 +** o Fixed a number of bugs, including CMPACC (reg direct) and ALU ops (reg +** direct) being decoded as same. +** +** 12/26/98 +** o Shortenend the PC to 12 bits from 16, to reduce design size. +** o REduced the datapath width to 8 from 16, to reduce design size. +** +** Lots of previous history not accounted for. Started to work on POPCORN +** about 12/12/98 +*/ diff --git a/projects/popcorn/src/popcorn.v b/projects/popcorn/src/popcorn.v new file mode 100644 index 0000000..7cf9bac --- /dev/null +++ b/projects/popcorn/src/popcorn.v @@ -0,0 +1,310 @@ +/* +** POPCORN +** +** This core adheres to GNU Public Licence +** Jeung Joon Lee *** www.cmosexod.com +** joon.lee@quantum.com +** +** JJL 12/12/98 +** updated 3/16/2000 +** +** This is an 8 bit general purpose microprocessor. The architecture +** is that of a CISC type with accumulator-based operations. +** The internal data-path is 8 bits, and the external bus is 8. +** No support for muliply or other complex arithmetic operations are +** supported (see ALU below). +** +** Two addressing modes: immediate and register-direct.f +** 23 instructions are supported +** +** Instrutions lengths can be 1 or 2 or 3 bytes, which correcponds to +** executions times of 2, 3 and 4 cycles. + +** to change the address width: from 12 bits to wider: +** change: +** AW to desired value (all 3 files) +** REG_OPHI width definition +** +*/ + +module popcorn( sys_clk, // system clock 1Mhz + sys_rst, // system reset active low + w_acc, // accumulator write signal, active low + w_ax, // AX regster write signal, active low + w_bx, // BX " " " + w_p, // PORT " " " + w_flag, // FLAG " " " + bbus_mux, // bbus mux selector + alu_func, + pc_mux, + addx_mux, + sp_mux, + flag_mux, + w_pc, + w_sp, + w_oplo, + w_ophi, + w_opl, + data_bus, + addx_bus, + port, + reg_opl, + reg_flag, + + c_bus, + b_bus, + d_bus, + data_bus_wr, + code_wr_l, + reg_acc, + reg_ax, + reg_bx, + reg_p + ); + +input sys_clk,sys_rst,w_acc,w_ax,w_bx,w_p,w_flag; +input pc_mux,w_pc,w_oplo,w_ophi,w_opl; +input [3:0] alu_func; +input [2:0] bbus_mux; +input data_bus_wr; +input code_wr_l; +output [7:0] b_bus,c_bus; +output [11:0] d_bus; +output [7:0] reg_opl; +output [2:0] reg_flag; +inout [7:0] data_bus; +inout [7:0] port; +output [11:0] addx_bus; +input sp_mux,w_sp; +input [1:0] addx_mux; +input flag_mux; +output [7:0] reg_acc; +output [7:0] reg_ax; +output [7:0] reg_bx; +output [7:0] reg_p; + +reg [7:0] reg_ax,reg_bx,reg_acc; +reg [11:0] reg_pc, reg_sp; +reg [7:0] reg_opl,reg_oplo,reg_p; +reg [3:0] reg_ophi; +reg carry,pos,zero,cout; +reg [7:0] b_bus,c_bus; +reg [11:0] addx_bus; +//reg [7:0] reg_port_dir; +wire [2:0] reg_flag; +wire [11:0] d_bus; +wire [7:0] data; +wire [7:0] input_port; +wire cs_reg_port_dir; +wire reg_port_gate; + +/* +** DATA BUS :: bi-directional +** Output c_bus onto the pad at data_bus_wr == 0 +*/ +assign data = data_bus; // input +assign data_bus = data_bus_wr ? 8'hzz : c_bus; //output + +/* +** REGISTER FILE :: Synchronous to sys_clk +** There are total of 6 registers +** ACC accumulator 8 bit +** AX general purpose register 8 bit +** BX general purpose register 8 bit +** P I/O port register 4 bit +*/ +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) + reg_p <= 8'h00; + else if(~w_p) + reg_p <= c_bus; + +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) + reg_ax <= 8'h00; + else if(~w_ax) + reg_ax <= c_bus; + +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) + reg_bx <= 8'h00; + else if(~w_bx) + reg_bx <= c_bus; + +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) + reg_acc <= 8'h00; + else if(~w_acc) + reg_acc <= c_bus; + + +/* OPL :: Synchronous to W_OPL +** Opcode_Latch register 8 bit +** Latched by rising edge of W_OPL +*/ +always @(posedge w_opl or negedge sys_rst) + if(~sys_rst) + reg_opl <= 8'h00; + else + reg_opl <= data; + +/* +** PC:: Synchronous to sys_clk +** Program Counter 8 bit +** +** Input: sys_clk : on rising edge, PC is update +** w_pc : 1=allow pc to change, 0=pc has its previous +** value +** pc_mux : 1=on the rising edge of sys_clk pc=pc+1 +** 0=on the rising edge of sys_clk pc=d_bus +*/ +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) + reg_pc <= 12'h000; // the reset value must be all 0's + else if (~w_pc & pc_mux) + reg_pc <= reg_pc + 1; + else if ( ~w_pc & ~pc_mux ) + reg_pc <= d_bus; + else + reg_pc <= reg_pc; + +/* +** SP :: Synchrnous to sys_clk +** Stack Pointer +*/ +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) + reg_sp <= 12'hFFF; // default reset value of sp is 0xFFF + else if (~w_sp & sp_mux) + reg_sp <= reg_sp - 1; + else if ( ~w_sp & ~sp_mux) + reg_sp <= reg_sp + 1; + +/* +** ADDX mux +*/ +always @(addx_mux or reg_pc or reg_sp or d_bus) + case (addx_mux) + 2'b00: addx_bus <= d_bus; + 2'b01: addx_bus <= reg_pc; + default: addx_bus <= reg_sp; + endcase + +/* +** Arithmetic Logic Unit +** This ALU provides 9 funtions: +** 0 c = a+b +** 1 c = a-b (a+~b+1) +** 2 c = a&b +** 3 c = a|b +** 4 c = a^b +** 5 c = ~a +** 6 c = a >> 1 +** 7 c = a << 1 +** 8 c = b +** else c = a +*/ +always @(reg_acc or b_bus or alu_func) begin + case (alu_func) + 4'b0000: {cout,c_bus} <= reg_acc+b_bus; + 4'b0001: {cout,c_bus} <= reg_acc-b_bus; + 4'b0010: begin c_bus <= reg_acc&b_bus; cout<=1'b0; end + 4'b0011: begin c_bus <= reg_acc|b_bus; cout<=1'b0; end + 4'b0100: begin c_bus <= reg_acc^b_bus; cout<=1'b0; end + 4'b0101: begin c_bus <= ~reg_acc; cout<=1'b0; end + 4'b0110: begin c_bus <= {1'b0,reg_acc[7:1]}; cout<=reg_acc[0]; end + 4'b0111: begin c_bus <= {reg_acc[6:0],1'b0}; cout<=reg_acc[7]; end + 4'b1000: begin c_bus <= b_bus; cout<=1'b0; end + default: begin c_bus <= reg_acc; cout<=1'b0; end + endcase +end + +// Define ALU flag register +always @(posedge sys_clk or negedge sys_rst) + if (~sys_rst) begin + pos <= 1'b0; + zero <= 1'b0; + carry <= 1'b0; + end + else if ((~w_flag) && (flag_mux==1'b1)) begin + zero <= ~( | c_bus ); // zero=1 iff all bits==0 + pos <= ~(c_bus[7]); // pos=1 iff msb=0 + carry <= cout; // carry , see alu above + end + else if ((~w_flag) && (flag_mux==1'b0)) begin + zero <= c_bus[5]; // zero=1 iff all bits==0 + pos <= c_bus[6]; // pos=1 iff msb=0 + carry <= c_bus[7]; // carry , see alu above + end + +assign reg_flag = {carry,pos,zero}; + +/* +** Define B MUX +*/ +always @(reg_ax or reg_bx or reg_flag or reg_pc or d_bus or reg_p or bbus_mux or input_port) + case (bbus_mux) + 3'b001: b_bus <= reg_ax; + 3'b010: b_bus <= reg_bx; + 3'b011: b_bus <= reg_pc[7:0]; + 3'b100: b_bus <= {reg_flag,1'b0,reg_pc[11:8]}; + 3'b101: b_bus <= input_port; + 3'b110: b_bus <= d_bus[7:0]; + default: b_bus <=d_bus; + endcase + + +/* +** Define d_bus +*/ +assign d_bus={reg_ophi[3:0],reg_oplo}; +always @(posedge w_oplo or negedge sys_rst) + if (~sys_rst) + reg_oplo <= 8'h00; + else + reg_oplo <= data; +always @(posedge w_ophi or negedge sys_rst) + if (~sys_rst) + reg_ophi <= 4'b0000; + else + reg_ophi <= data[3:0]; + + +/* +** Define the PORT register :: Synchronous to sys_clk +*/ +/* +assign port[0] = reg_port_dir[0] ? 1'bz : reg_p[0]; +assign port[1] = reg_port_dir[1] ? 1'bz : reg_p[1]; +assign port[2] = reg_port_dir[2] ? 1'bz : reg_p[2]; +assign port[3] = reg_port_dir[3] ? 1'bz : reg_p[3]; +assign port[4] = reg_port_dir[4] ? 1'bz : reg_p[4]; +assign port[5] = reg_port_dir[5] ? 1'bz : reg_p[5]; +assign port[6] = reg_port_dir[6] ? 1'bz : reg_p[6]; +assign port[7] = reg_port_dir[7] ? 1'bz : reg_p[7]; +*/ + +assign input_port = port; +//assign port = reg_port_gate ? 8'hzz : reg_p; + +bufif1(port[0], reg_p[0], reg_port_gate); +bufif1(port[1], reg_p[1], reg_port_gate); +bufif1(port[2], reg_p[2], reg_port_gate); +bufif1(port[3], reg_p[3], reg_port_gate); +bufif1(port[4], reg_p[4], reg_port_gate); +bufif1(port[5], reg_p[5], reg_port_gate); +bufif1(port[6], reg_p[6], reg_port_gate); +bufif1(port[7], reg_p[7], reg_port_gate); + +assign reg_port_gate = (bbus_mux[2:0]==3'b101); + +/* +assign cs_reg_port_dir = ( ~(addx_bus==12'hD00) | (~code_wr_l) ); +always @(posedge cs_reg_port_dir or negedge sys_rst) + if (~sys_rst) + reg_port_dir <= 8'h00; + else + reg_port_dir <= c_bus; +*/ +endmodule + diff --git a/projects/popcorn/src/sequencer.v b/projects/popcorn/src/sequencer.v new file mode 100644 index 0000000..1157b67 --- /dev/null +++ b/projects/popcorn/src/sequencer.v @@ -0,0 +1,521 @@ +/* +** SEQUENCER +** +** This core adheres to GNU Public Licence +** Jeung Joon Lee *** www.cmosexod.com +** joon.lee@quantum.com +** +** JJL 12/12/98 +** updated 3/16/2000 +** +** +** This is the sequencer for the POPCORN 8bit microprocessor. +** Controls all of the control signals. +** +** This sequencer controls these following signals of the datapath "popcorn" +** +** +** +*/ +module sequencer (sys_clk, + phase_1_clk, + sys_rst, + w_acc, + w_ax, + w_bx, + w_p, + w_flag, + bbus_mux, + alu_func, + pc_mux, + addx_mux, + sp_mux, + flag_mux, + w_sp, + w_pc, + w_oplo, + w_ophi, + w_opl, + reg_opl, + code_cs_l, + code_rd_l, + sram_cs_l, + code_wr_l, + reg_flag, + data_bus_wr, + next_state + ); + +input sys_clk,sys_rst,phase_1_clk; +input [7:0] reg_opl; +input [2:0] reg_flag; +output w_acc,w_ax,w_bx,w_p,w_flag,pc_mux,w_pc; +output w_opl,w_oplo,w_ophi,code_cs_l,code_rd_l; +output [3:0] alu_func; +output [2:0] bbus_mux; +output [3:0] next_state; +output [1:0] addx_mux; +output sp_mux, w_sp, sram_cs_l, code_wr_l, data_bus_wr; +output flag_mux; + +reg [5:0] next_state; +reg code_cs_l,code_rd_l_gate; +reg w_opl_gate,w_acc_gate,w_ax_gate,w_bx_gate; +reg w_p_gate,w_flag_gate,pc_mux; +reg w_oplo_gate,w_ophi_gate,w_pc_gate; +reg [3:0] alu_func; +reg [2:0] bbus_mux; +reg dummy; +reg [1:0] addx_mux; +reg w_sp_gate, sp_mux, sram_cs_l, code_wr_l_gate,data_bus_wr; +reg flag_mux; + +parameter state_s0 =6'b000001; +parameter state_s1 =6'b000010; +parameter state_s2 =6'b000100; +parameter state_s3 =6'b001000; +parameter state_s4 =6'b010000; +parameter state_s5 =6'b100000; + +assign w_acc = w_acc_gate; +assign w_ax = w_ax_gate; +assign w_bx = w_bx_gate; +assign w_p = w_p_gate; +assign w_flag= w_flag_gate; +assign w_pc = w_pc_gate; +assign w_sp = w_sp_gate; +assign w_oplo= w_oplo_gate | phase_1_clk; // asynchronous reg +assign w_ophi= w_ophi_gate | phase_1_clk; // asynchronous reg +assign w_opl = w_opl_gate | phase_1_clk; // asynchronous reg +assign code_rd_l = code_rd_l_gate | phase_1_clk; // code rom rd +assign code_wr_l = code_wr_l_gate | phase_1_clk; + +/* +** Behavioral description of the Sequencer State machine +** +** synchronous state machine, transitions on the rising edge of sys_clk +*/ +always @(posedge sys_clk or negedge sys_rst) begin + if (~sys_rst) begin // rst + code_cs_l <= 1'b1; + code_rd_l_gate <= 1'b1; + code_wr_l_gate <= 1'b1; + sram_cs_l <= 1'b1; + alu_func <= 4'b0000; + w_acc_gate <= 1'b1; + w_ax_gate <= 1'b1; + w_bx_gate <= 1'b1; + w_p_gate <= 1'b1; + w_flag_gate <= 1'b1; + w_opl_gate <= 1'b1; + w_oplo_gate <= 1'b1; + w_ophi_gate <= 1'b1; + bbus_mux <= 2'b00; + addx_mux <= 2'b01; + pc_mux <= 1'b1; + sp_mux <= 1'b1; + flag_mux <= 1'b1; + w_pc_gate <= 1'b1; + w_sp_gate <= 1'b1; + data_bus_wr <= 1'b1; +// port_bus_ena <= 1'b0; + next_state <= state_s0; + end + else case(next_state) + /* + ** FETCH OPCODE STATE. (at beginning of s1 opcode is latched into reg_opl) + */ + state_s0: begin // *****2 + code_cs_l <= 1'b0; // enable code cs + code_rd_l_gate <= 1'b0; // enable code rd + code_wr_l_gate <= 1'b1; + sram_cs_l <= 1'b1; + alu_func <= 4'bx; + w_acc_gate <= 1'b1; + w_ax_gate <= 1'b1; + w_bx_gate <= 1'b1; + w_p_gate <= 1'b1; + w_flag_gate <= 1'b1; + w_opl_gate <= 1'b0; // latch opl at s1 + w_oplo_gate <= 1'b1; + w_ophi_gate <= 1'b1; + bbus_mux <= 2'b00; + addx_mux <= 2'b01; + pc_mux <= 1'b1; + sp_mux <= 1'b1; + flag_mux <= 1'b1; + w_pc_gate <= 1'b1; + w_sp_gate <= 1'b1; + data_bus_wr <= 1'b1; + //port_bus_ena <= 1'b0; + next_state <= state_s1; + end + /* + ** REGISTER DIRECT INSTRUCTIONS 1 byte, 2 cycle instructions + ** these are: stacc, ldacc, and alu ops + */ + state_s1: begin // ******** 2 + + begin + code_cs_l <= 1'b1; // disable cs and rd from code ram + code_rd_l_gate <= 1'b1; + w_opl_gate <= 1'b1; // disable opcode latch gate + end + + // ALU opcode - register direct + if((reg_opl[7:6]==2'b00)&&(reg_opl[2:0]!=3'b111)) begin + alu_func <= reg_opl[6:3]; + w_acc_gate <= 1'b0; // latch acc with result on s0 + w_flag_gate <= 1'b0; // latch reg result on s0 + bbus_mux <= reg_opl[2:0]; + w_pc_gate <= 1'b0; // increment pc on s0 + next_state <= state_s0; + end + // CMPACC opcode - register direct + else if((reg_opl[7:3]==5'b01000)&&(reg_opl[2:0]!=3'b111)) begin + alu_func <= 4'b0001; // alu subtract + w_flag_gate <= 1'b0; // latch reg result on s0 + bbus_mux <= reg_opl[2:0]; + w_pc_gate <= 1'b0; // increment pc on s0 + next_state <= state_s0; + end + // LDACC - register direct + else if ((reg_opl[7:3]==5'b10001)&&(reg_opl[2:0]!=3'b111)) begin + alu_func <= 4'b1000; // alu=select b bus + w_acc_gate <= 1'b0; // latch acc on s0 + bbus_mux <= reg_opl[2:0]; + w_pc_gate <= 1'b0; // increment pc on s0 + next_state <= state_s0; + end + // STACC - register direct + else if ((reg_opl[7:3]==5'b10010)&&(reg_opl[2:0]!=3'b111)) begin + alu_func <= 4'b1001; // alu=select a bus + case (reg_opl[2:0]) // latch approp dest reg on s0 + 3'b001: w_ax_gate <= 1'b0; + 3'b010: w_bx_gate <= 1'b0; + 3'b100: begin + w_flag_gate <= 1'b0; + flag_mux <= 1'b0; + end + 3'b101: w_p_gate <= 1'b0; + default: dummy <= 1'b0; // do nothing + endcase + bbus_mux <= 3'b001; // anything but 3'b101 (port) + w_pc_gate <= 1'b0; // increment pc in s0 + next_state <= state_s0; + end + // PUSH , write out to [SP] and decrement SP + // push does this: [SP] <- reg, SP=SP-1 + else if(reg_opl[7:3]==5'b01110) begin + if (reg_opl[2:0]==3'b000) begin + alu_func <= 4'b1001; // select c_bus=acc + end else begin + alu_func <= 4'b1000; // select c_bus=b_bus + end + bbus_mux <= reg_opl[2:0]; + sp_mux <= 1'b1; // sp = sp -1 + w_sp_gate <= 1'b0; // allow sp to decrement + addx_mux <= 2'b10; // select sp as addx source + sram_cs_l <= 1'b0; // select sram + code_wr_l_gate <= 1'b0; // and write + data_bus_wr <= 1'b0; // drive data bus with c_bus + w_pc_gate <= 1'b0; // increment pc on s0 + next_state <= state_s0; + end + // POP, RET part I, increment SP + // pop does this: + // SP=SP+1 + // reg_ophi <- [SP] + // SP=SP+1 + // reg_oplo <- [SP] + else if (reg_opl[7:3]==5'b01111 | reg_opl[7:0]==8'b11011110) begin + alu_func <= 4'b1000; // c_bus = b_bus + bbus_mux <= 3'b110; // b_bus = d_bus + sp_mux <= 1'b0; // sp = sp + 1 + w_sp_gate <= 1'b0; // allow sp to increment + next_state <= state_s2; + end + + // Immediate mode, so need to go and fetch one or two operands. + // setup the PC so it increments to 1 and cs and rd gates + // are asserted + else begin + alu_func <= 4'b1000; + w_acc_gate <= 1'b1; + w_ax_gate <= 1'b1; + w_bx_gate <= 1'b1; + w_p_gate <= 1'b1; + w_oplo_gate <= 1'b1; + bbus_mux <= 3'b110; // show d_bus on c_bus + pc_mux <= 1'b1; + w_pc_gate <= 1'b0; // increment pc on s2 + next_state <= state_s2; + end + end + + /* + ** IMMEDIATE INSTRUCTIONS. 2 bytes, 3 cycle insruction + ** these are: ldi, alu ops. + */ + state_s2: begin + // LDI - load immediate 8 bit value to register + if ((reg_opl[7:6]==2'b10)&&(reg_opl[2:0]==3'b111)) begin + alu_func <= 4'b1000; + case (reg_opl[5:3]) // latch the dest reg on s0 + 3'b000: w_acc_gate <= 1'b0; + 3'b001: w_ax_gate <= 1'b0; + 3'b010: w_bx_gate <= 1'b0; + 3'b101: w_p_gate <= 1'b0; + default: dummy <= 1'b0; //do nothing + endcase + bbus_mux <= 3'b110; + w_pc_gate <= 1'b0; // increment pc on s0 + code_cs_l <= 1'b0; // allow -cs and -rd to code ram + code_rd_l_gate <= 1'b0; + w_oplo_gate <= 1'b0; // assert oplo gate + next_state <= state_s0; + end + // ALU op - immediate + else if ((reg_opl[7:6]==2'b00)&&(reg_opl[2:0]==3'b111)) begin + alu_func <= reg_opl[6:3]; // get alu oprerationg from instruction + w_acc_gate <= 1'b0; // latch acc on s0 + w_flag_gate <= 1'b0; // update flag on s0 + bbus_mux <= 3'b110; + w_pc_gate <= 1'b0; // increment pc on s0 + code_cs_l <= 1'b0; // allow -cs and -rd to code ram + code_rd_l_gate <= 1'b0; + w_oplo_gate <= 1'b0; // assert oplo gate + next_state <= state_s0; + end + // CMPACC op - immediate + else if ((reg_opl[7:3]==5'b01000)&&(reg_opl[2:0]==3'b111)) begin + alu_func <= 4'b001; // alu to subtract + w_flag_gate <= 1'b0; // update flag on s0 + bbus_mux <= 3'b110; // bbus select d_bus + w_pc_gate <= 1'b0; // increment pc on s0 + code_cs_l <= 1'b0; // allow -cs and -rd to code ram + code_rd_l_gate <= 1'b0; + w_oplo_gate <= 1'b0; // assert oplo gate + next_state <= state_s0; + end + // POP, part II, load data from [SP] + else if (reg_opl[7:3]==5'b01111) begin + code_cs_l <= 1'b1; // don't read from code ram + alu_func <= 4'b1000; // c_bus = b_bus + bbus_mux <= 3'b110; // b_bus = d_bus + w_sp_gate <= 1'b1; // disallow sp to change + addx_mux <= 2'b10; // select sp as addx source + sram_cs_l <= 1'b0; // select sram + code_rd_l_gate <= 1'b0; // and read + w_oplo_gate <= 1'b0; // and latch it into reg_oplo + case (reg_opl[2:0]) + 3'b000: w_acc_gate <= 1'b0; + 3'b001: w_ax_gate <= 1'b0; + 3'b010: w_bx_gate <= 1'b0; + 3'b100: begin + w_flag_gate <= 1'b0; + flag_mux <= 1'b0; + end + default: w_p_gate <= 1'b0; + endcase + w_pc_gate <= 1'b0; // increment pc on s0 + next_state <= state_s0; + end + // RET, part II, load hi byte of saved PC + else if (reg_opl[7:0]==8'b11011110) begin + code_cs_l <= 1'b1; // don't read from code ram + alu_func <= 4'b1000; // c_bus = b_bus + bbus_mux <= 3'b110; // b_bus = d_bus + sp_mux <= 1'b0; // increment SP + w_sp_gate <= 1'b0; // allow sp to change + addx_mux <= 2'b10; // select sp as addx source + sram_cs_l <= 1'b0; // select sram + code_rd_l_gate <= 1'b0; // and read + w_ophi_gate <= 1'b0; // and latch it into reg_ophi + code_rd_l_gate <= 1'b0; + w_oplo_gate <= 1'b0; // assert oplo gate + next_state <= state_s3; + end + // these are the 3 byte branching instructions. so go and fetch the high + // byte operand + else begin + code_cs_l <= 1'b0; // allow -cs and -rd to code ram + code_rd_l_gate <= 1'b0; + alu_func <= 4'b1000; + w_acc_gate <= 1'b1; + w_ax_gate <= 1'b1; + w_bx_gate <= 1'b1; + w_p_gate <= 1'b1; + bbus_mux <= 3'b110; + pc_mux <= 1'b1; + w_pc_gate <= 1'b0; // increment pc on s3 + w_oplo_gate <= 1'b0; // assert oplo gate + next_state <= state_s3; + end + end + + /* + ** BRANCHING INSTRUCTIONS, 3 bytes, 4 cycles + */ + state_s3: begin +// w_ophi_gate <= 1'b0; // latch-in high byte operand +// w_oplo_gate <= 1'b1; // deassert oplo gate + // Branching operations - immediate + if((reg_opl[7]==1'b1)&&(reg_opl[2:0]==3'b000)) begin + alu_func <= 4'b1000; // for debug, c_bus=b_bus + bbus_mux <= 3'b110; // for debug c_bus=d_bus + case(reg_opl[6:3]) + 4'b0011: if(reg_flag[0]==1'b1) // JE + pc_mux<=0; + else pc_mux<=1; + 4'b0100: if(reg_flag[0]==1'b0) // JNE + pc_mux<=0; + else pc_mux<=1; + 4'b0101: if(reg_flag[1]==1'b1) // JP + pc_mux<=0; + else pc_mux<=1; + 4'b0110: if(reg_flag[1]==1'b0) // JN + pc_mux<=0; + else pc_mux<=1; + 4'b0111: if(reg_flag[2]==1'b1) // JC + pc_mux<=0; + else pc_mux<=1; + 4'b1000: if(reg_flag[2]==1'b0) // JNC + pc_mux<=0; + else pc_mux<=1; + 4'b1001: pc_mux<=0; // JMP unconditional + default: pc_mux<=pc_mux; + endcase + w_pc_gate <= 1'b0; // increment pc on s0 + w_ophi_gate <= 1'b0; // latch-in high byte operand + w_oplo_gate <= 1'b1; // deassert oplo gate + next_state <= state_s0; + end + // RET, part III, load lo byte of saved PC + else if (reg_opl[7:0]==8'b11011110) begin + code_cs_l <= 1'b1; // don't read from code ram + alu_func <= 4'b1000; // c_bus = b_bus + bbus_mux <= 3'b100; // b_bus = reg_pc hi + sp_mux <= 1'b1; // increment SP + w_sp_gate <= 1'b1; // disallow sp to change + addx_mux <= 2'b10; // select sp as addx source + sram_cs_l <= 1'b0; // select sram + code_rd_l_gate <= 1'b0; // and read + w_ophi_gate <= 1'b1; + w_oplo_gate <= 1'b0; // and latch it into reg_oplo + pc_mux <= 1'b0; + w_pc_gate <= 1'b0; // PC = d_bus + w_flag_gate <= 1'b0; // restore flag + next_state <= state_s0; + end + // CALL, part I. Allow the PC to be incremented so, that + // the PC that is pushed onto the stack is pointing to + // the instruction after the call + // what CALL does: + // [SP] <- PC low byte + // SP=SP-1 + // [SP] <- PC high 4 bits + // SP=SP-1 + else if (reg_opl[7:0]==8'b11010110) begin + w_pc_gate <= 1'b0; + w_ophi_gate <= 1'b0; // latch-in high byte operand + w_oplo_gate <= 1'b1; // deassert oplo gate + next_state <= state_s4; + end + // Must be direct addresing instruction + else begin + next_state <= state_s4; + w_ophi_gate <= 1'b0; // latch-in high byte operand + w_oplo_gate <= 1'b1; // deassert oplo gate + w_pc_gate <= 1'b1; // deassert PC gate. + end + end + + /* + ** DIRECT INSTRUCTIONS, 3 byte, 5 cycles + */ + state_s4: begin + + begin + code_cs_l <= 1'b1; + w_ophi_gate <= 1'b1; // latch-in high byte operand + end + + // LMD and STM instructions + if ((reg_opl[7:4]==5'b0110)&&(reg_opl[2:0]!=3'b111)) begin + sram_cs_l <= 1'b0; // select the sram + addx_mux <= 2'b00; // select the sram address + w_pc_gate <= 1'b0; // increment pc on s0 + pc_mux <= 1'b1; + alu_func <= 4'b1000; // else c_bus = b_bus + bbus_mux <= 3'b110; // select d_bus by default + // if it is STM + if (reg_opl[3]==1'b1) begin + // while in STM, select c_bus=a_bus only if it's STM acc,xx + if (reg_opl[2:0]==3'b000) begin + alu_func <= 4'b1001; // if DDD=acc c_bus=acc + end + bbus_mux <= reg_opl[2:0]; // select the approp reg + code_wr_l_gate <= 1'b0; + data_bus_wr <= 1'b0; // drive data bus with c_bus + end + // if it is LDM + else begin + code_rd_l_gate <= 1'b0; + w_oplo_gate <= 1'b0; // latchin oplo with sram data on s4 + code_rd_l_gate <= 1'b1; + case (reg_opl[2:0]) // latch approp dest reg on s0 + 3'b001: w_ax_gate <= 1'b0; + 3'b010: w_bx_gate <= 1'b0; + 3'b101: w_p_gate <= 1'b0; + 3'b100: begin + w_flag_gate <= 1'b0; + flag_mux <= 1'b0; + end + default: w_acc_gate <= 1'b0; // else, must be acc + endcase + end + next_state <= state_s0; + end // LMD if + // CALL, 3 bytes, 6 cycles + else if (reg_opl[7:0]==8'b11010110) begin + code_rd_l_gate <= 1'b1; + alu_func <= 4'b1000; // select c_bus=b_bus + bbus_mux <= 3'b011; // select reg_pc low + sp_mux <= 1'b1; // sp = sp - 1 on s5 + w_sp_gate <= 1'b0; // allow sp to decrement + addx_mux <= 2'b10; // select sp as addx source + sram_cs_l <= 1'b0; // select sram + code_wr_l_gate <= 1'b0; // and write + data_bus_wr <= 1'b0; // drive data bus with c_bus + w_pc_gate <= 1'b1; // + next_state <= state_s5; + end // call + end // state_s4 + + /* + ** CALL, 3 byte, 6 cycles + ** save to [SP], [SP-1] PC+1 not PC + */ + state_s5: begin + if (reg_opl[7:0]==8'b11010110) begin + alu_func <= 4'b1000; // select c_bus=b_bus + bbus_mux <= 3'b100; // select reg_pc hi + sp_mux <= 1'b1; // sp = sp - 1 on s0 + w_sp_gate <= 1'b0; // allow sp to decrement + addx_mux <= 2'b10; // select sp as addx source + sram_cs_l <= 1'b0; // select sram + code_wr_l_gate <= 1'b0; // and write + data_bus_wr <= 1'b0; // drive data bus with c_bus + pc_mux <= 1'b0; // PC = d_bus + w_pc_gate <= 1'b0; // allow pc to be changed + next_state <= state_s0; + end + end // state_s5 + + endcase //state machine + +end + + +endmodule diff --git a/projects/popcorn/syn/ise/project/pc.lso b/projects/popcorn/syn/ise/project/pc.lso new file mode 100644 index 0000000..22de730 --- /dev/null +++ b/projects/popcorn/syn/ise/project/pc.lso @@ -0,0 +1 @@ +work diff --git a/projects/popcorn/syn/ise/project/pc.prj b/projects/popcorn/syn/ise/project/pc.prj new file mode 100644 index 0000000..cae8efc --- /dev/null +++ b/projects/popcorn/syn/ise/project/pc.prj @@ -0,0 +1,3 @@ +verilog work "popcorn.v" +verilog work "sequencer.v" +verilog work "pc.v" diff --git a/projects/popcorn/syn/ise/project/pc_vhdl.prj b/projects/popcorn/syn/ise/project/pc_vhdl.prj new file mode 100644 index 0000000..e69de29 diff --git a/projects/popcorn/syn/ise/project/popcorn.ise b/projects/popcorn/syn/ise/project/popcorn.ise new file mode 100644 index 0000000..bcdd219 Binary files /dev/null and b/projects/popcorn/syn/ise/project/popcorn.ise differ