// // SYNTHETIC PIC 3.0 5/1/98 // // This is a synthesizable Microchip 16C57 compatible // microcontroller. This core is not intended as a high fidelity model of // the PIC, but simply a way to offer a simple processor core to people // familiar with the PIC who also have PIC tools. // // pictest.v - top-level testbench (NOT SYNTHESIZABLE) // piccpu.v - top-level synthesizable module // picregs.v - register file instantiated under piccpu // picalu.v - ALU instantiated under piccpu // picidec.v - Instruction Decoder instantiated under piccpu // picdram.v - Memory model for the DATA memory (e.g. Register File) // picpram.v - Memory model for the PROGRAM memory. // convert.pl - Perl script used to translate MPLAB's "Disassembled Code" output // into the Verilog $readmemh compatible file // test*.asm - (note the wildcard..) Several test programs used // to help debug the verilog. I used MPLAB and the simulator // to develop these programs and get the expected results. // Then, I ran them on Verilog-XL where they appeared to // match. // // Copyright, Tom Coonan, '97. // Use freely, but not for resale as is. You may use this in your // own projects as desired. Just don't try to sell it as is! // // module piccpu ( clk, reset, enable, paddr, pdata, portain, portbout, portcout, expdin, expdout, expaddr, expread, expwrite, debugw, debugpc, debuginst, debugstatus, q1, q2, q3, q4 ); // Define this for faster throughput using only Q1,Q4 phases. // // If defined, this causes the phase shifter to skip from Q1 to Q4 and bypass Q2, Q3. // The q2 and q3 are still present but are never asserted. // // The original PIC uses 4 phases. This design only requires Q1 and Q4 to operate. // By only using the 2 phases, we get 1 Instruction cycle per 2 clocks instead // of per 4 clocks. // `define ONLY_USE_2_PHASES // Basic Core I/O. input clk; input reset; input enable; // Program memory interface output [10:0] paddr; input [11:0] pdata; // Basic I/O Ports input [7:0] portain; output [7:0] portbout; output [7:0] portcout; // Expansion Interface input [7:0] expdin; // Data from expansion circuits TO the PIC Core output [7:0] expdout; // Data to the expansion circuits FROM the PIC Core output [6:0] expaddr; // File address output expread; // Active high read signal (read FROM expansion circuit) output expwrite; // Active high write signal (write TO expansion circuit) // Debugging output [7:0] debugw; output [10:0] debugpc; output [11:0] debuginst; output [7:0] debugstatus; // Phases are output for use by testbench. Also, program memory needs q1. output q1; output q2; output q3; output q4; // Register declarations for outputs reg [10:0] paddr; reg [7:0] portbout; reg [7:0] portcout; reg [7:0] expdout; reg [6:0] expaddr; reg expread; reg expwrite; // Multiphase // // Saying that something occurs "in Q1" means that that something get registered // on the rising edge of clk when Q1 is high. So, this latching actually occurs // just as Q1 goes low again. If you wanted something to be done/progated "during" // Q1, then you would use Q4 and the posedge of clk. // // Phase Operation // Q4 Latch output of program memory into INST, Increment PC, latch all other registers. // Q1 Start read of Register File operand. // Q2 Start read of program memory // Q3 .. nothing .. reg [3:0] phase; wire q1,q2,q3,q4; always @(posedge clk) begin if (reset) begin phase <= 4'b0000; end else begin // synopsys translate_off `ifdef ONLY_USE_2_PHASES // synopsys translate_on case (phase) // synopsys full_case parallel_case 4'b0000: phase <= 4'b0001; 4'b1000: phase <= 4'b0001; // Hop from Q1 right to Q4! 4'b0001: phase <= 4'b1000; default: phase <= 4'b0000; endcase // synopsys translate_off `else case (phase) // synopsys full_case parallel_case 4'b0000: phase <= 4'b0001; 4'b1000: phase <= 4'b0100; 4'b0100: phase <= 4'b0010; 4'b0010: phase <= 4'b0001; 4'b0001: phase <= 4'b1000; default: phase <= 4'b0000; endcase `endif // synopsys translate_on end end // synopsys translate_off initial begin `ifdef ONLY_USE_2_PHASES $display ("ONLY_USE_2_PHASES is defined, using phases Q1 and Q4"); `else $display ("ONLY_USE_2_PHASES is NOT defined, using all 4 phases Q1,Q2,Q3 and Q4"); `endif end // synopsys translate_on assign q1 = phase[3]; assign q2 = phase[2]; assign q3 = phase[1]; assign q4 = phase[0]; // This should be set to the ROM location where our restart vector is. // As set here, we have 512 words of program space. // parameter RESET_VECTOR = 11'h7FF; parameter INDF_ADDRESS = 3'h0, TMR0_ADDRESS = 3'h1, PCL_ADDRESS = 3'h2, STATUS_ADDRESS = 3'h3, FSR_ADDRESS = 3'h4, PORTA_ADDRESS = 3'h5, PORTB_ADDRESS = 3'h6, PORTC_ADDRESS = 3'h7; // ********* Special internal registers // Instruction Register reg [11:0] inst; // Program Counter reg [10:0] pc; // Stack Registers and Stack "levels" register. reg [ 1:0] stacklevel; reg [10:0] stack1; reg [10:0] stack2; // W Register reg [ 7:0] w; // The STATUS register (#3) is 8 bits wide, however, we only currently use 2 bits // of it; the C and Z bit. // // bit 0 - C // bit 2 - Z // reg [ 7:0] status; // The FSR register is the pointer register used for Indirect addressing (e.g. using INDF). reg [ 7:0] fsr; // Timer 0 reg [ 7:0] tmr0; reg [ 7:0] prescaler; // Option Register reg [7:0] option; // Tristate Control registers. We do not neccessarily support bidirectional ports, but // will save a place for these registers and the TRIS instruction. Use for debug. reg [7:0] trisa; reg [7:0] trisb; reg [7:0] trisc; // I/O Port registers // reg [7:0] porta; // Input PORT reg [7:0] portb; // Output PORT reg [7:0] portc; // Output PORT // ********** Instruction Related signals ****** reg skip; // When HI force a NOP (all zeros) into inst // Derive special sub signals from inst register wire [ 7:0] k; wire [ 4:0] fsel; wire d; wire [ 2:0] b; // ********** File Address ************ // // This is the 7-bit Data Address that includes the lower 5-bit fsel, the // FSR bits and any indirect addressing. // Use this bus to address the Register File as well as all special registers, etc. // reg [6:0] fileaddr; // Address Selects reg specialsel; reg regfilesel; reg expsel; // ****** Instruction Decoder Outputs ************** // Write enable for the actual ZERO and CARRY bits within the status register. // Generated by the Instruction Decoder. // wire [1:0] aluasel; wire [1:0] alubsel; wire [3:0] aluop; wire zwe; wire cwe; wire isoption; wire istris; wire fwe; // High if any "register" is being written to at all. wire wwe; // Write Enable for the W register. Produced by Instruction Decoder. // ************* Internal Busses, mux connections, etc. ******************** // Bit decoder bits. reg [7:0] bd; // Final decoder value after any polarity inversion. reg [7:0] bdec; // e.g. "Bit Decoded" wire bdpol; // Polarity bit for the bit test operations. // Data in and out of the and out of the register file // reg [7:0] regfilein; // Input into Register File, is connected to the dbus. wire [7:0] regfileout; // Path leaving the register file, goes to SBUS Mux reg regfilewe; // Write Enable reg regfilere; // Read Enable // // The dbus (Destination Bus) comes out of the ALU. It is available to all // writable registers. // // The sbus (Source Bus) comes from all readable registers as well as the output // of the Register File. It is one of the primary inputs into the ALU muxes. // // The (Expansion Bus) is another potential source. See the 'exp' signals. // reg [7:0] dbus; reg [7:0] sbus; // ALU Signals // reg [7:0] alua; reg [7:0] alub; wire [7:0] aluout; wire alucin; wire alucout; wire aluz; // ALU A and B mux selects. // parameter [1:0] ALUASEL_W = 2'b00, ALUASEL_SBUS = 2'b01, ALUASEL_K = 2'b10, ALUASEL_BD = 2'b11; parameter [1:0] ALUBSEL_W = 2'b00, ALUBSEL_SBUS = 2'b01, ALUBSEL_K = 2'b10, ALUBSEL_1 = 2'b11; // ALU Operation codes. // parameter [3:0] ALUOP_ADD = 4'b0000; parameter [3:0] ALUOP_SUB = 4'b1000; parameter [3:0] ALUOP_AND = 4'b0001; parameter [3:0] ALUOP_OR = 4'b0010; parameter [3:0] ALUOP_XOR = 4'b0011; parameter [3:0] ALUOP_COM = 4'b0100; parameter [3:0] ALUOP_ROR = 4'b0101; parameter [3:0] ALUOP_ROL = 4'b0110; parameter [3:0] ALUOP_SWAP = 4'b0111; // Instantiate each of our subcomponents // picregs regs ( .clk (clk), .reset (reset), .we (regfilewe), .re (regfilere), .bank (fileaddr[6:5]), .location (fileaddr[4:0]), .din (regfilein), .dout (regfileout) ); // Instatiate the ALU. // picalu alu ( .op (aluop), .a (alua), .b (alub), .y (aluout), .cin (status[0]), .cout (alucout), .zout (aluz) ); // Instantiate the Instruction Decoder. This is really just a lookup table. // Given the instruction, generate all the signals we need. // // For example, each instruction implies a specific ALU operation. Some of // these are obvious such as the ADDW uses an ADD alu op. Less obvious is // that a mov instruction uses an OR op which lets us do a simple copy. // // Data has to funnel through the ALU, which sometimes makes for contrived // ALU ops. // wire idecwwe; wire idecfwe; wire ideczwe; wire ideccwe; picidec idec ( .inst (inst), .aluasel (aluasel), .alubsel (alubsel), .aluop (aluop), .wwe (idecwwe), .fwe (idecfwe), .zwe (ideczwe), .cwe (ideccwe), .bdpol (bdpol), .option (isoption), .tris (istris) ); // Inhibit all the write enables when the core is disabled. // assign wwe = enable & idecwwe; assign fwe = enable & idecfwe; assign zwe = enable & ideczwe; assign cwe = enable & ideccwe; // *********** Debug **************** assign debugw = w; assign debugpc = pc; assign debuginst = inst; assign debugstatus = status; // *********** REGISTER FILE Addressing **************** // // We implement the following: // - The 5-bit fsel address is within a "BANK" which is 32 bytes. // - The FSR bits 6:5 are the BANK select, so there are 4 BANKS, a // total of 128 bytes. Minus the 8 special registers, that's // really 120 bytes. // - The INDF register is for indirect addressing. Referencing INDF // uses FSR as the pointer. Therefore, using INDF/FSR you address // 7-bits of memory. // We DO NOT currently implement the PAGE for program (e.g. STATUS register // bits 6:5) // // The fsel address *may* be zero in which case, we are to do indirect // addressing, using FSR register as the 8-bit pointer. // // Otherwise, use the 5-bits of FSEL (from the Instruction itself) and // 2 bank bits from the FSR register (bits 6:5). // always @(fsel or fsr or status) begin if (fsel == INDF_ADDRESS) begin // The INDEX register is addressed. There really is no INDEX register. // Use the FSR as an index, e.g. the FSR contents are the fsel. // fileaddr <= fsr[6:0]; end else begin // Use FSEL field and status bank select bits // fileaddr <= {status[6:5], fsel}; end end // Write Enable to Register File. // Assert this when the general fwe (write enable to *any* register) is true AND Register File // address range is specified. // always @(regfilesel or fwe or q4) regfilewe <= q4 & regfilesel & fwe; // Read Enable (this if nothing else, helps in debug.) // Assert if Register File address range is specified AND the ALU is actually using some // data off the SBUS. // always @(regfilesel or aluasel or alubsel or q1) regfilere <= q1 & regfilesel & ((aluasel == ALUASEL_SBUS) | (alubsel == ALUBSEL_SBUS)); // *********** Address Decodes ************** // // Generate 3 selects: specialsel, regfilesel and expsel // // ** NOTE: Must change this whenever more or few expansion addresses are // added or removed from the memory map. Otherwise, the dbus mux // won't be controlled properly! // always @(fileaddr) begin casex (fileaddr) // synopsys full_case parallel_case // This shouldn't really change. // 7'bXX00XXX: // The SPECIAL Registers are lower 8 addresses, in ALL BANKS begin specialsel <= 1'b1; regfilesel <= 1'b0; expsel <= 1'b0; end // Adjust this case as EXPANSION locations change! // 7'b111111X: // EXPANSION Registers are the top (2) addresses begin specialsel <= 1'b0; regfilesel <= 1'b0; expsel <= 1'b1; end // Assume everything else must be the Register File.. // default: begin specialsel <= 1'b0; regfilesel <= 1'b1; expsel <= 1'b0; end endcase end // *********** Expansion Interface ************** always @(dbus) expdout <= dbus; always @(fileaddr) expaddr <= fileaddr; always @(q1 or expsel or aluasel or alubsel) expread <= q1 & expsel & ((aluasel == ALUASEL_SBUS) | (alubsel == ALUBSEL_SBUS)); always @(q4 or expsel or fwe) expwrite <= q4 & expsel & fwe; // // *********** SBUS ************** // The sbus (Source Bus) is the output of a multiplexor that takes // inputs from the Register File, and all other special registers // and input ports. The Source Bus then, one of the inputs to the ALU // First MUX selects from all the special regsiters // always @(fsel or fsr or tmr0 or pc or status or porta or portb or portc or regfileout or expdin or specialsel or regfilesel or expsel) begin // For our current mix of registers and peripherals, only the first 8 addresses // are "special" registers (e.g. not in the Register File). As more peripheral // registers are added, they must be muxed into this MUX as well. // // We currently prohibit tristates. // // if (specialsel) begin // Special register case (fsel[2:0]) // synopsys parallel_case full_case 3'h0: sbus <= fsr; 3'h1: sbus <= tmr0; 3'h2: sbus <= pc[7:0]; 3'h3: sbus <= status; 3'h4: sbus <= fsr; 3'h5: sbus <= porta; // PORTA is an input-only port 3'h6: sbus <= portb; // PORTB is an output-only port 3'h7: sbus <= portc; // PORTC is an output-only port endcase end else begin // // Put whatever address equation is neccessary here. Remember to remove unnecessary // memory elements from Register File (picregs.v). It'll still work, but they'd be // wasted flip-flops. // if (expsel) begin sbus <= expdin; end else begin if (regfilesel) begin // Final Priority is Choose the register file sbus <= regfileout; end else begin sbus <= 8'h00; end end end end // ************** DBUS ****** // The Destination bus is just the output of the ALU. // always @(aluout) dbus <= aluout; always @(dbus) regfilein <= dbus; // Drive the ROM address bus straight from the PC // always @(pc) paddr = pc; // Define sub-signals out of inst // assign k = inst[7:0]; assign fsel = inst[4:0]; assign d = inst[5]; assign b = inst[7:5]; // Bit Decoder. // // Simply take the 3-bit b field in the PIC instruction and create the // expanded 8-bit decoder field, which is used as a mask. // always @(b) begin case (b) 3'b000: bdec <= 8'b00000001; 3'b001: bdec <= 8'b00000010; 3'b010: bdec <= 8'b00000100; 3'b011: bdec <= 8'b00001000; 3'b100: bdec <= 8'b00010000; 3'b101: bdec <= 8'b00100000; 3'b110: bdec <= 8'b01000000; 3'b111: bdec <= 8'b10000000; endcase end always @(bdec or bdpol) bd <= bdec ^ bdpol; // Instruction regsiter usually get the ROM data as its input, but // sometimes for branching, the skip signal must cause a NOP. // always @(posedge clk) begin if (reset) begin inst <= 12'h000; end else begin if (enable & q4) begin if (skip == 1'b1) begin inst <= 12'b000000000000; // FORCE NOP end else begin inst <= pdata; end end end end // SKIP signal. // // We want to insert the NOP instruction for the following conditions: // GOTO,CALL and RETLW instructions (All have inst[11:10] = 2'b10 // BTFSS instruction when aluz is HI ( // BTFSC instruction when aluz is LO // always @(inst or aluz) begin casex ({inst, aluz}) 13'b10??_????_????_?: // A GOTO, CALL or RETLW instructions skip <= 1'b1; 13'b0110_????_????_1: // BTFSC instruction and aluz == 1 skip <= 1'b1; 13'b0111_????_????_0: // BTFSS instruction and aluz == 0 skip <= 1'b1; 13'b0010_11??_????_1: // DECFSZ instruction and aluz == 1 skip <= 1'b1; 13'b0011_11??_????_1: // INCFSZ instruction and aluz == 1 skip <= 1'b1; default: skip <= 1'b0; endcase end // 4:1 Data MUX into alua // // always @(aluasel or w or sbus or k or bd) begin case (aluasel) 2'b00: alua <= w; 2'b01: alua <= sbus; 2'b10: alua <= k; 2'b11: alua <= bd; endcase end // 4:1 Data MUX into alub // // always @(alubsel or w or sbus or k) begin case (alubsel) 2'b00: alub <= w; 2'b01: alub <= sbus; 2'b10: alub <= k; 2'b11: alub <= 8'b00000001; endcase end // W Register always @(posedge clk) begin if (reset) begin w <= 8'h00; end else begin if (wwe & q4) begin w <= dbus; end end end // ************ Writes to various Special Registers (fsel between 0 and 7) // INDF Register (Register #0) // // Not a real register. This is the Indirect Addressing mode register. // See the regfileaddr logic. // TMR0 Register (Register #1) // // Timer0 is currently only a free-running timer clocked by the main system clock. // always @(posedge clk) begin if (reset) begin tmr0 <= 8'h00; end else begin if (q4) begin // See if the status register is actually being written to if (fwe & specialsel & (fileaddr[2:0] == TMR0_ADDRESS)) begin // Yes, so just update the register from the dbus tmr0 <= dbus; end else begin // OPTION[3] - Assigns prescaler to either WDT or TIMER0. We don't implement WDT. // If this bit is 0, then use the prescaler. If 1 then increment. // // Mask off the prescaler value based on desired divide ratio. // Whenever this is zero, then that is our divided pulse. Increment // the final timer value when it's zero. // casex (option[3:0]) // synopsys parallel_case full_case 4'b1XXX: tmr0 <= tmr0 + 1; 4'b0000: if (~|(prescaler & 8'b00000001)) tmr0 <= tmr0 + 1; 4'b0001: if (~|(prescaler & 8'b00000011)) tmr0 <= tmr0 + 1; 4'b0010: if (~|(prescaler & 8'b00000111)) tmr0 <= tmr0 + 1; 4'b0011: if (~|(prescaler & 8'b00001111)) tmr0 <= tmr0 + 1; 4'b0100: if (~|(prescaler & 8'b00011111)) tmr0 <= tmr0 + 1; 4'b0101: if (~|(prescaler & 8'b00111111)) tmr0 <= tmr0 + 1; 4'b0110: if (~|(prescaler & 8'b01111111)) tmr0 <= tmr0 + 1; 4'b0111: if (~|(prescaler & 8'b11111111)) tmr0 <= tmr0 + 1; endcase end end end end // The prescaler is always counting from 00 to FF always @(posedge clk) begin if (reset) begin prescaler <= 8'h00; end else begin if (q4) begin // See if the status register is actually being written to prescaler <= prescaler + 1; end end end // PCL Register (Register #2) // // PC Lower 8 bits. This is handled in the PC section below... // STATUS Register (Register #3) // always @(posedge clk) begin if (reset) begin status <= 8'h00; end else begin if (q4) begin // See if the status register is actually being written to if (fwe & specialsel & (fileaddr[2:0] == STATUS_ADDRESS)) begin // Yes, so just update the register from the dbus status <= dbus; end else begin // Update status register on a bit-by-bit basis. // // For the carry and zero flags, each instruction has its own rule as // to whether to update this flag or not. The instruction decoder is // providing us with an enable for C and Z. Use this to decide whether // to retain the existing value, or update with the new alu status output. // status <= { status[7], // BIT 7: Undefined.. (maybe use for debugging) status[6], // BIT 6: Program Page, HI bit status[5], // BIT 5: Program Page, LO bit status[4], // BIT 4: Time Out bit (not implemented at this time) status[3], // BIT 3: Power Down bit (not implemented at this time) (zwe) ? aluz : status[2], // BIT 2: Z status[1], // BIT 1: DC (cwe) ? alucout : status[0] // BIT 0: C }; end end end end // FSR Register (Register #4) // always @(posedge clk) begin if (reset) begin fsr <= 8'h00; end else begin // See if the status register is actually being written to if (q4 & fwe & specialsel & (fileaddr[2:0] == FSR_ADDRESS)) begin fsr <= dbus; end end end // OPTION Register // // The special OPTION instruction should move W into the OPTION register. always @(posedge clk) begin if (reset) begin option <= 8'h00; end else begin if (q4 & isoption) option <= dbus; end end // PORTA Input Port (Register #5) // // Register anything on the module's porta input on every single clock. // always @(posedge clk) begin if (reset) begin porta <= 8'h00; end else begin if (q1) begin porta <= portain; end end end // PORTB Output Port (Register #6) always @(posedge clk) begin if (reset) begin portb <= 8'h00; end else begin if (q4 & fwe & specialsel & (fileaddr[2:0] == PORTB_ADDRESS) & ~istris) begin portb <= dbus; end end end // Connect the output ports to the register output. always @(portb) portbout <= portb; // PORTC Output Port (Register #7) always @(posedge clk) begin if (reset) begin portc <= 8'h00; end else begin if (q4 & fwe & specialsel & (fileaddr[2:0] == PORTC_ADDRESS) & ~istris) begin portc <= dbus; end end end // Connect the output ports to the register output. always @(portc) portcout <= portc; // TRIS Registers always @(posedge clk) begin if (reset) begin trisa <= 8'hff; // Default is to tristate them end else begin if (q4 & fwe & specialsel & (fileaddr[2:0] == PORTA_ADDRESS) & istris) begin trisa <= dbus; end end end always @(posedge clk) begin if (reset) begin trisb <= 8'hff; // Default is to tristate them end else begin if (q4 & fwe & specialsel & (fileaddr[2:0] == PORTB_ADDRESS) & istris) begin trisb <= dbus; end end end always @(posedge clk) begin if (reset) begin trisc <= 8'hff; // Default is to tristate them end else begin if (q4 & fwe & specialsel & (fileaddr[2:0] == PORTC_ADDRESS) & istris) begin trisc <= dbus; end end end // ********** PC AND STACK ************************* // // There are 4 ways to change the PC. They are: // GOTO 101k_kkkk_kkkk // CALL 1001_kkkk_kkkk // RETLW 1000_kkkk_kkkk // MOVF 0010_0010_0010 (e.g. a write to reg #2) // MOVWF 0000_0010_0010 (write from W to reg #2) // // Remember that the skip instructions work by inserting // a NOP instruction or not into program stream and don't // change the PC. // // Implmenent PC // // always @(posedge clk) begin if (reset == 1'b1) begin pc <= RESET_VECTOR; end else begin if (q4 & enable) begin casex ({inst, stacklevel}) 14'b101?_????_????_??: pc <= {status[6:5], inst[8:0]}; // GOTO 14'b1001_????_????_??: pc <= {status[6:5], 1'b0, inst[7:0]}; // CALL 14'b1000_????_????_00: pc <= stack1; // RETLW 14'b1000_????_????_01: pc <= stack1; // RETLW 14'b1000_????_????_10: pc <= stack2; // RETLW 14'b1000_????_????_11: pc <= stack2; // RETLW 14'b00?0_0010_0010_??: pc <= {pc[10:8], dbus}; // MOVF or MOVWF where f=PC default: pc <= pc + 1; endcase end end end // Implement STACK1 and STACK2 registers // // The Stack registers are only fed from the PC itself! // always @(posedge clk) begin if (reset) begin stack1 <= 9'h000; end else begin if (q4 & enable) begin // CALL instruction if (inst[11:8] == 4'b1001) begin case (stacklevel) 2'b00: // No previous CALLs begin stack1 <= pc; $display ("Write to STACK1: %0h", pc); end 2'b01: // ONE previous CALL begin stack2 <= pc; $display ("Write to STACK2: %0h", pc); end 2'b10: // TWO previous CALLs -- This is illegal on the 16C5X! begin $display ("Too many CALLs!!"); end 2'b11: begin $display ("Too many CALLs!!"); end endcase end end end end // Write to stacklevel // // The stacklevel register keeps track of the current stack depth. On this // puny processor, there are only 2 levels (you could fiddle with this and // increase the stack depth). There are two stack registers, stack1 and stack2. // The stack1 register is used first (e.g. the first time a call is performed), // then stack2. As CALLs are done, stacklevel increments. Conversely, as // RETs are done, stacklevel goes down. always @(posedge clk) begin if (reset == 1'b1) begin stacklevel <= 2'b00; // On reset, there should be no CALLs in progress end else begin if (q4 & enable) begin casex ({inst, stacklevel}) // Call instructions 14'b1001_????_????_00: stacklevel <= 2'b01; // Record 1st CALL 14'b1001_????_????_01: stacklevel <= 2'b10; // Record 2nd CALL 14'b1001_????_????_10: stacklevel <= 2'b10; // Already 2! Ignore 14'b1001_????_????_11: stacklevel <= 2'b00; // {shouldn't happen} // Return instructions 14'b1000_????_????_00: stacklevel <= 2'b00; // {shouldn't happen} 14'b1000_????_????_01: stacklevel <= 2'b00; // Go back to no CALL in progress 14'b1000_????_????_10: stacklevel <= 2'b01; // Go back to 1 CALL in progress 14'b1000_????_????_11: stacklevel <= 2'b10; // {shouldn't happen} sort of like, Go back to 2 calls in progress default: stacklevel <= stacklevel; endcase end end end endmodule