/* qs5_mix.v: Cycle-accurate mixed Behavioral/RTL Verilog Model for qs5 =================================================================================== | Copyright 1998-1999 by John Rible, | | Permission is granted for individual non-commercial use, provided this notice | | is included. Contact the author concerning any other use. | =================================================================================== This 'Baby RISC' (BRISC) processor has the following characteristics: - 8-bit data bus and 15-bit address bus (32KB) with 16-bit opcodes - two-cycle, two-operand register operations, 3 or 4 cycles for load/store - one memory access and one ALU operation during each clock cycle - 8 visible registers including flags, program counter and return address - boot mode which loads RAM from the 8-bit I/O port - most instructions are conditional, reducing need for branches - four memory register locations and four 8-bit I/O port addresses - pre-modify write and post-modify read address modes =================================================================================== | My Verilog name conventions: - combinatorial signals have lower-case names | | 60 lines per page, 2-up - registers and flipflops have UPPERCASE NAMES | | 84 characters per line - functions and tasks have Capitalized Names | ----------------------------------------------------------------------------------- 11jun99 jr: alu & regfile to separate modules, clean up a bit more 31mar99 jr: add 99q1 changes, remove incrementer, reorganize as qs5 30dec98 jr: converted from behavioral qs4_high. =================================================================================*/ `timescale 1ns/1ns // `define big_fast_alu // comment out this line for the small_slow_alu module qs5( clock, boot, reset, req, pd, po, ack, dir, ma, md, csb, oeb, web ); // control interface pins input clock; wire clock; // slow enough for everything to settle input boot; wire boot; // high: copy data from port to mem input reset; wire reset; // high: reboot/restart processor // host port interface pins input req; wire req; // host handshake line inout [7:0] pd; wire [7:0] pd; // data to/from host output [1:0] po; wire [1:0] po; // data to host (for XESS board) output ack; wire ack; // chip handshake line output dir; wire dir; // high: chip->host data transfer // memory interface pins inout [14:0] ma; wire [14:0] ma; // 32Kx8 sram address pins inout [7:0] md; wire [7:0] md; // 32Kx8 sram data pins output csb; wire csb; // active-low sram chip select output oeb; wire oeb; // active-low sram read enable output web; wire web; // active-low sram write enable // State bits reg BOOT1, RESET1, REQ1; // synchronizing FFs reg BOOT2, RESET2; // processor state FFs `define pstate {BOOT2,RESET2} // \ / `define Powerup 2'b11 `define Booting 2'b10 `define Restart 2'b01 `define Running 2'b00 reg RISE; // true: 1st clock that REQUEST is high reg HIGH; // true: high byte of word reg SECOND; // true: second part of memory opcode reg ZERO; // TRUE: TS is zero reg SAVED_OK; // holds value of ok throughout opcode // FL register bits reg REQUEST; // input (FL[3]) reg ACKNOWLEDGE, DIRECTION; // output (FL[2:1]) reg CARRY; // Carry bit (FL[0]) // invisible registers reg [15:0] H, I; // hold & instruction registers reg [7:0] M; // memory data register reg [15:1] N; // next address register reg [7:0] P; // port data register // internal data nets reg [15:0] dH, dHI; // holding-register inputs reg [7:0] dM; // memory-register input reg [15:0] fl_reg; // flag register output reg [15:0] x_reg, y_reg; // all registers reg [15:0] w_bus, o_bus, x_bus, y_bus, a_bus; // datapath busses // datapath selector nets reg [3:1] srx; // register-file read/write address reg [3:1] sry; // register-file read-only address `define FL 3'h0 // flags register `define G1 3'h1 // general register `define G2 3'h2 // general register `define G3 3'h3 // general register `define G4 3'h4 // general register `define TS 3'h5 // general register, testable `define RA 3'h6 // general register, return address `define PC 3'h7 // program counter reg sw; // datapath bus selector `define I_Addr 1'b1 `define x_reg 1'b0 reg sx; // datapath bus selector `define o_bus 1'b1 `define w_bus 1'b0 reg so; // datapath bus selector `define H_Data 1'b1 `define y_reg 1'b0 // so,sy reg sy; // datapath bus selector `define I_Data 1'b1 reg sh; // holding register selector `define new 1'b1 `define old 1'b0 reg [2:1] si; // I_Data literal selector `define zero 0 `define one 1 `define two 2 `define short 3 reg [4:1] op; // alu opcode `define adn 4'h0 // co = ci z = x + y `define sbn 4'h1 // co = ci z = x - y `define mov 4'h2 // co = ci z = 0 + y `define neg 4'h3 // co = ci z = 0 - y `define add 4'h4 // co,z = x + y `define sub 4'h5 // co,z = x - y `define adc 4'h6 // co,z = x + y + ci `define sbb 4'h7 // co,z = x - y - ci `define and 4'h8 // co = ci z = x & y `define inv 4'h9 // co = ci z = ~y `define or 4'ha // co = ci z = x | y `define xor 4'hb // co = ci z = x ^ y `define sr 4'hc // co = ci z = ci,y >>1 `define sl 4'hd // co = ci z = y <<1 `define rrc 4'he // co = y[0] z = ci,y >>1 `define rlc 4'hf // co = y[15] z = y,ci <<1 reg next; // true: last clock of 1st half of memory opcode reg done; // true: last clock of opcode reg ok; // true: opcode condition is met; execute opcode reg direct; // true: data address is in opcode, not in register reg immediate; // true: data value is after opcode, not in register reg byte; // true: opcode does 8-bit data access reg return; // true: opcode does prefetch from RA, not PC reg forward_ok; // true: value in H will be used as new PC reg io; // true: opcode accesses host port, not memory reg in_ok; // true: opcode will input from host port this cycle reg out_ok; // true: opcode will output to host port this cycle wire [15:0] z_bus; // alu output wire co; // alu carry-out reg ec; // true: enable carry-bit write reg ez; // true: enable register write wire [15:0] x_out, y_out; // register file outputs reg oe, we, cs; // active-high memory control signals // opcode field definitions ======================================================= `define ii 15:14 // branch operations `define bf 2'b00 // co = ci z = x + y `define bb 2'b01 // co = ci z = x - y `define br 2'b1x // co = ci z = y `define iiii 15:12 // alu operations (see alu_op) `define b 14 // load/store bytes `define word 1'b0 // 16-bit access `define byte 1'b1 // 8-bit access `define aa 13:12 // direct address `define mi 13:12 // modify an indirect address `define same 2'b0x // address is unchanged `define fwd 2'b10 // offset is added `define back 2'b11 // offset is subtracted `define uuuuuu 13:8 // branch distance `define uuuu 11:8 // short literal `define yyy 10:8 // Ry register (see sry) `define r 7 // memory read/write `define wr 1'b0 // write access `define rd 1'b1 // read access `define f 7 // alu source `define literal 1'b0 // from opcode literal `define register 1'b1 // from register `define xxx 6:4 // Rx register (see srx) `define cc 3:2 // condition code `define all 2'b00 // always execute opcode `define ret 2'b01 // always execute opcode and return `define z 2'b10 // execute opcode if r1==0 `define nz 2'b11 // execute opcode if r1!=0 `define op 1:0 // opcode state `define call_op 2'bx0 // call opcode `define alu_ops 2'b01 // alu opcodes `define mem_ops 2'b11 // memory opcodes // Datapath ======================================================================= always @(posedge clock) begin: registers // memory bus-cycle holding registers dM = (BOOT2|in_ok)? pd: md; // select input source M <= dM; // hold for next clock N <= a_bus[15:1]; // hold for address+1 // instruction/data holding registers dH = {dM,M}; // assemble 16-bit input value dHI = (BOOT2 || sh==`new)? dH: H; H <= dHI; // hold next data/instruction I <= done? dHI: I; // hold for duration of opcode // port register P <= RESET2? 8'hFF: out_ok? o_bus[7:0]: P; // programmer-visible registers ACKNOWLEDGE <= (ez && srx==`FL)? z_bus[2]: ACKNOWLEDGE; DIRECTION <= (ez && srx==`FL)? z_bus[1]: DIRECTION; CARRY <= (ez && srx==`FL)? z_bus[0]: ec? co: CARRY; ZERO <= (ez && srx==`TS)? (z_bus==0): ZERO; // update when TS register changes end // registers // instantiate register file RAM8X16D regfile(clock, ez, z_bus, srx, sry, x_out, y_out); // datapath combinatorial logic (busses and alu) always @( RESET2 or BOOT2 or SECOND or H or I or N or HIGH or REQUEST or ACKNOWLEDGE or DIRECTION or CARRY or x_out or y_out or srx or sry or sw or so or sx or sy or si or byte or op ) begin: busses fl_reg = {12'b0,REQUEST,ACKNOWLEDGE,DIRECTION,CARRY}; x_reg = (srx==0)? fl_reg: x_out; y_reg = (sry==0)? fl_reg: y_out; w_bus = (sw==`I_Addr)? I_Addr(I): x_reg; o_bus = (so==`H_Data)? H_Data(H): y_reg; x_bus = (sx==`o_bus)? o_bus: w_bus; y_bus = (sy==`I_Data)? I_Data(I): y_reg; a_bus = HIGH? {N,1'b1}: x_bus; // {N,1} 'increments' an even value end // busses qs_alu alu(op, x_bus, y_bus, CARRY, co, z_bus); // instantiate the alu // Datapath functions ============================================================= function [15:0] H_Data; input [15:0] Hold; if (RESET2) H_Data = 0; else if (BOOT2 | byte & SECOND) H_Data = {8'b0,Hold[15:8]}; else H_Data = Hold; endfunction // H_Data returns the value held in H function [15:0] I_Addr; input [15:0] Op; case (Op[`aa]) 0: I_Addr = 8; 1: I_Addr = 10; 2: I_Addr = 12; 3: I_Addr = 14; default: I_Addr = 16'bx; endcase endfunction // I_Addr returns the encoded direct address value function [15:0] I_Data; input [15:0] Op; case (si) 0: I_Data = 0; // no address change 1: I_Data = 1; // byte stride 2: I_Data = 2; // word stride `short: if (Op[`xxx]==`PC) I_Data = Op[`uuuuuu]<<1; // branch distance else I_Data = Op[`uuuu]; // short literal default: I_Data = 16'bx; endcase endfunction // I_Data returns the encoded data value // bidirect & output pin assignments ============================================== assign web = ~we | clock | RESET2; assign oeb = ~oe | clock; assign csb = ~cs | clock; assign ma = a_bus[14:0]; assign md = web==0? (HIGH? o_bus[15:8]: o_bus[7:0]): 8'bz; assign ack = BOOT2? REQUEST: ACKNOWLEDGE; assign dir = BOOT2? 0: DIRECTION; assign po = P[1:0]; // output for XESS board assign pd = (req & dir)? {P[7:2],2'bz}: 8'bz; // cautiously drive the port bus // State machines 1 =============================================================== always @(posedge clock) begin: UpdateState BOOT1 <= boot; RESET1 <= reset; REQ1 <= req; // synchronize inputs BOOT2 <= BOOT1; RESET2 <= RESET1; REQUEST <= REQ1; // with the clock RISE <= REQ1==1 && REQUEST==0; // signal the rising edge of REQUEST HIGH <= BOOT2|next|done? 0: 1; // high-byte of 2-byte operations SECOND <= next|SECOND&~done; // second opcycle of memory opcodes SAVED_OK <= ok; // save test condition end // UpdateState // State machine: tasks and functions ------------------------------------ task WriteCycle; input WriteOk; begin we = WriteOk; oe = 0; cs = 1; end endtask // WriteCycle sets memory control signals for a memory write task ReadCycle; begin we = 0; oe = 1; cs = 1; end endtask // ReadCycle sets memory control signals for a memory read function NewOk; input [15:0] Op; case (Op[`cc]) `all, `ret: NewOk = 1; `z: NewOk = ZERO; `nz: NewOk =~ZERO; default: NewOk = 1'bx; endcase endfunction // NewOk returns true when the condition specified by the opcode is met function PointerOk; input [15:0] Op; PointerOk = (immediate & ~byte) | (ok & ~direct); endfunction // PointerOk returns true when it's ok to modify the data pointer function Forward; input [15:0] Op; Forward = return? Op[`yyy]==`RA: Op[`yyy]==`PC; endfunction // Forward returns true when the value in H can be used for the new PC // State machines 2 =============================================================== // functions, continued function [2:1] Stride; input [15:0] Op; casex (Op[`mi]) `same: Stride = 0; `fwd, `back: Stride = Op[`b]? 1: 2; default: Stride = 2'bx; endcase endfunction // Stride is the value used to modify pointer addresses function [4:1] AluOp; input [15:0] Op; if (Op[`xxx]==`PC) // branches casex (Op[`ii]) `bf: AluOp = `adn; `bb: AluOp = `sbn; `br: AluOp = `mov; default: AluOp = 4'bx; endcase else AluOp = Op[`iiii]; endfunction // AluOp returns the opcode for the register instructions function [4:1] MemOp; input [15:0] Op; casex (Op[`mi]) `same, `fwd: MemOp = `adn; `back: MemOp = `sbn; default: MemOp = 4'bx; endcase endfunction // MemOp returns the opcode for the load/store instructions // Instruction decode: booting ==================================================== always @ (BOOT2 or RESET2 or RISE or SECOND or HIGH or SAVED_OK or H or I or ZERO ) begin: NextState // set default values so case statements won't infer registers srx = 'bx; sx = 'bx; sw = 'bx; so = 'bx; sry = 'bx; sy = 'bx; si = 'bx; op = 'bx; sh = 'bx; next = 0; done = 0; ez = 0; ec = 0; io = 0; in_ok = 0; out_ok = 0; // generate some common code factors. PARTIAL DECODE ONLY! direct = (I[`xxx]==`FL); immediate = (I[`xxx]==`PC); byte = (I[`b]==`byte); return = (I[`cc]==`ret); ok = RESET2? 0: SECOND|HIGH? SAVED_OK: NewOk(I); forward_ok = ok & Forward(I); case (`pstate) // processor state machine `Powerup: begin srx = `PC; sx = `o_bus; so = `H_Data; sy = `I_Data; si = 0; op = `adn; ez = 1; ec = 1; ReadCycle; end `Booting: if (RISE==0) begin // boot read srx = `PC; sx = `w_bus; sw = `x_reg; sh = `new; ReadCycle; end else begin // boot write srx = `PC; sx = `w_bus; sw = `x_reg; so = `H_Data; sy = `I_Data; si = 1; op = `adn; ez = 1; WriteCycle(1); end // Instruction decode: restart & register opcodes ================================= `Restart: if (HIGH==0) begin // low byte srx = `PC; sx = `o_bus; so = `H_Data; sy = `I_Data; si = 2; op = `adn; ez = 1; ReadCycle; end else begin // high byte sh = `new; done = 1; ReadCycle; end `Running: casex (I[`op]) // opcodes `call_op: if (HIGH==0) begin // low byte srx = `RA; sry = `PC; sx = `o_bus; so = `H_Data; sy = `y_reg; op = `mov; ez = 1; sh = `old; ReadCycle; end else begin // high byte srx = `PC; sx = `o_bus; so = `H_Data; sy = `I_Data; si = 2; op = `adn; ez = 1; sh = `new; done = 1; ReadCycle; end `alu_ops: if (HIGH==0) begin // low byte srx = `PC; sry = `RA; sx = return? `o_bus: `w_bus; so = `y_reg; sw = `x_reg; sy = `I_Data; si = 2; op = `adn; ez = 1; ReadCycle; end else begin // high byte srx = I[`xxx]; sry = I[`yyy]; sx = `w_bus; sw = `x_reg; sy = (I[`f]==`register)? `y_reg: `I_Data; si = `short; op = AluOp(I); ez = ok; ec = ok; sh = `new; done = 1; ReadCycle; end // Instruction decode: memory write opcodes ====================================== `mem_ops: if (I[`r]==`wr) // write opcodes if (SECOND==0) // write fetch is first opcycle if (HIGH==0) begin // low byte srx = `PC; sry = `RA; sx = return? `o_bus: `w_bus; so = `y_reg; sw = `x_reg; sy = `I_Data; si = 2; op = `adn; ez = 1; ReadCycle; end else begin // high byte srx = I[`xxx]; sx = `w_bus; sw = `x_reg; sy = `I_Data; si = Stride(I); op = MemOp(I); ez = PointerOk(I); sh = `new; next = 1; ReadCycle; end else // write data is second opcycle if (HIGH==0) begin // low byte srx = I[`xxx]; sry = I[`yyy]; sx = `w_bus; sw = direct? `I_Addr: `x_reg; so = `y_reg; sh = `old; done = byte; io = direct & byte; out_ok = io & ok; WriteCycle(ok & ~io); end else begin // high byte sry = I[`yyy]; so = `y_reg; sh = `old; done = 1; WriteCycle(ok); end // Instruction decode: memory read opcodes ======================================= else // read opcodes if (SECOND==0) // read data is first opcycle if (HIGH==0) begin // low byte srx = I[`xxx]; sx = `w_bus; sw = direct? `I_Addr: `x_reg; sy = `I_Data; si = Stride(I); op = MemOp(I); ez = PointerOk(I); sh = `new; next = byte; io = direct & byte; in_ok = io & ok; ReadCycle; end else begin // high byte sh = `new; next = 1; ReadCycle; end else // read fetch is second opcycle if (HIGH==0) begin // low byte srx = `PC; sry = `RA; sx = (return | forward_ok)? `o_bus: `w_bus; so = forward_ok? `H_Data: `y_reg; sw = `x_reg; sy = `I_Data; si = 2; op = `add; ez = 1; sh = `old; ReadCycle; end else begin // high byte srx = I[`yyy]; sx = `o_bus; so = `H_Data; sy = `I_Data; si = 0; op = `add; ez = (ok & ~forward_ok); sh = `new; done = 1; ReadCycle; end default: $display("opcode default case at %0d",$time); endcase // opcodes default: $display("processor default case at %0d",$time); endcase // processor end // NextState endmodule // qs5 // external modules 1 ============================================================= module RAM8X16D(clk, we, in, adr, dadr, out, dout); input clk; wire clk; input we; wire we; input [15:0] in; wire [15:0] in; input [3:1] adr, dadr; wire [3:1] adr, dadr; output [15:0] out, dout; reg [15:0] out, dout; reg [15:0] RAM [0:7]; // (FL), G1, G2, G3, G4, TS, RA, PC registers always @(posedge clk) if (we) RAM[adr] <= in; always @(adr or dadr or RAM[adr] or RAM[dadr]) {out,dout} = {RAM[adr],RAM[dadr]}; // assign out = RAM[adr]; // assign dout = RAM[dadr]; endmodule // RAM8X16D module qs_alu (opcode, xin, yin, cin, cout, zout); input [4:1] opcode; wire [4:1] opcode; input [15:0] xin, yin; wire [15:0] xin, yin; input cin; wire cin; output cout; wire cout; output [15:0] zout; wire [15:0] zout; assign {cout,zout} = SimpleALU(opcode, xin, yin, cin); `ifdef big_fast_alu initial $display("Using big, fast behavioral ALU"); function [16:0] SimpleALU; input [4:1] opcode; input [15:0] x, y; input ci; reg co; reg [15:0] z; begin // synthesizing this leads to a big, fast ALU co=ci; case (opcode) `adn: z = x + y; `and: z = x & y; `sbn: z = x - y; `inv: z = ~y; `mov: z = 0 + y; `or : z = x | y; `neg: z = 0 - y; `xor: z = x ^ y; `add: {co,z} = x + y; `sr : z = {ci,y[15:1]}; `sub: {co,z} = x - y; `sl : z = {y[14:0],1'b0}; `adc: {co,z} = x + y + ci; `rrc: {z,co} = {ci,y}; `sbb: {co,z} = x - y - ci; `rlc: {co,z} = {y,ci}; default: {co,z} = 17'bx; endcase SimpleALU = {co,z}; end endfunction // SimpleALU returns the 17-bit alu result `else initial $display("Using small, slow ALU from schematic"); // external modules 2 ============================================================= function [16:0] SimpleALU; input [4:1] opcode; input [15:0] x, y; input ci; reg co, ylr, ylb, m2z, x0, c0; reg [6:1] control; reg [15:0] z, yl, yr, yy, xx, cc, maj, eor; reg [16:0] c; integer i; begin case (opcode) // synthesis full_case parallel_case // ylr ylb m2z x0 c0 c[0] // alu behavior // \ | | / `adn: control = {4'b0_0_0_0, 1'b0, 1'b0}; // z = x + y; `sbn: control = {4'b0_1_0_0, 1'b0, 1'b1}; // z = x - y; `mov: control = {4'b0_0_0_1, 1'b0, 1'b0}; // z = 0 + y; `neg: control = {4'b0_1_0_1, 1'b0, 1'b1}; // z = 0 - y; `add: control = {4'b0_0_0_0, 1'b0, 1'b0}; // {co,z} = x + y; `sub: control = {4'b0_1_0_0, 1'b0, 1'b1}; // {co,z} = x - y; `adc: control = {4'b0_0_0_0, 1'b0, ci}; // {co,z} = x + y + ci; `sbb: control = {4'b0_1_0_0, 1'b0, ~ci}; // {co,z} = x - y - ci; `and: control = {4'b0_0_1_0, 1'bx, 1'b0}; // z = x & y; `inv: control = {4'b0_1_1_1, 1'bx, 1'b1}; // z = ~y; `or : control = {4'b0_0_1_0, 1'bx, 1'b1}; // z = x | y; `xor: control = {4'b0_0_0_0, 1'b1, 1'bx}; // z = x ^ y; `sr : control = {4'b1_0_1_1, ci, 1'b1}; // z = {ci,y[15:1]}; `sl : control = {4'b1_1_1_1, 1'b0, 1'b1}; // z = {y[14:0],1'b0}; `rrc: control = {4'b1_0_1_1, ci, 1'b1}; // {z,co} = {ci,y}; `rlc: control = {4'b1_1_1_1, ci, 1'b1}; // {co,z} = {y,ci}; default: control = {4'bx_x_x_x, 1'bx, 1'bx}; endcase {ylr,ylb, m2z,x0, c0, c[0]} = control; // break out control bits yl = {y[14:0],c0}; // left-shifted y input yr = {c0,y[15:1]}; // right-shifted y input for (i=0; i<16; i=i+1) begin yy[i] = ylr? (ylb? yl[i]: yr[i]): (ylb? ~y[i]: y[i]); // 4x1 mux xx[i] = x0? 0: x[i]; cc[i] = c0? 0: c[i]; maj[i] = xx[i]? (yy[i]? 1'b1: c[i]): (yy[i]? c[i]: 1'b0); // 4x1 mux eor[i] = xx[i]? (yy[i]? cc[i]: ~cc[i]): (yy[i]? ~cc[i]: cc[i]); // 4x1 mux z[i] = m2z? maj[i]: eor[i]; // 2x1 mux c[i+1] = m2z? c[i]: maj[i]; // 2x1 mux end // for case (opcode) `adn: co = ci; `add: co = c[16]; `and: co = ci; `sr : co = ci; `sbn: co = ci; `sub: co = ~c[16]; `inv: co = ci; `sl : co = ci; `mov: co = ci; `adc: co = c[16]; `or : co = ci; `rrc: co = y[0]; `neg: co = ci; `sbb: co = ~c[16]; `xor: co = ci; `rlc: co = y[15]; default: co = 1'bx; endcase SimpleALU = {co,z}; end endfunction // SimpleALU returns the 17-bit alu result `endif endmodule // qs_alu