📄 cpu.v
字号:
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) // synopsys parallel_case 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; endcaseendalways @(bdec or bdpol) bd = (bdpol) ? ~bdec : bdec;// 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 (skip == 1'b1) begin inst <= 12'b000000000000; // FORCE NOP end else begin inst <= pdata; end endend// 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}) // synopsys parallel_case 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; endcaseend// 4:1 Data MUX into alua////always @(aluasel or w or sbus or k or bd) begin case (aluasel) // synopsys parallel_case 2'b00: alua = w; 2'b01: alua = sbus; 2'b10: alua = k; 2'b11: alua = bd; endcaseend// 4:1 Data MUX into alub////always @(alubsel or w or sbus or k) begin case (alubsel) // synopsys parallel_case 2'b00: alub = w; 2'b01: alub = sbus; 2'b10: alub = k; 2'b11: alub = 8'b00000001; endcaseend// W Registeralways @(posedge clk) begin if (reset) begin w <= 8'h00; end else begin if (wwe) 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 // 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 if (~option[5]) 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 endend// The prescaler is always counting from 00 to FF whenever OPTION[5] is cleared (e.g. T0CS)always @(posedge clk) begin if (reset) begin prescaler <= 8'h00; end else begin if (~option[5]) begin prescaler <= prescaler + 1; end endend// PCL Register (Register #2)//// PC Lower 8 bits. This is handled in the PC section below...// STATUS Register (Register #3)//parameter STATUS_RESET_VALUE = 8'h18;always @(posedge clk) begin if (reset) begin status <= STATUS_RESET_VALUE; end else 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 endend// 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 (fwe & specialsel & (fileaddr[2:0] == FSR_ADDRESS)) begin fsr <= dbus; end endend// OPTION Register//// The special OPTION instruction should move W into the OPTION register.//parameter OPTION_RESET_VALUE = 8'h3F;always @(posedge clk) begin if (reset) begin option <= OPTION_RESET_VALUE; end else begin if (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) if (reset) porta <= 8'h00; else porta <= portain;// PORTB Output Port (Register #6)always @(posedge clk) begin if (reset) begin portb <= 8'h00; end else begin if (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 (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 Registersalways @(posedge clk) begin if (reset) begin trisa <= 8'hff; // Default is to tristate them end else begin if (fwe & specialsel & (fileaddr[2:0] == PORTA_ADDRESS) & istris) begin trisa <= dbus; end end endalways @(posedge clk) begin if (reset) begin trisb <= 8'hff; // Default is to tristate them end else begin if (fwe & specialsel & (fileaddr[2:0] == PORTB_ADDRESS) & istris) begin trisb <= dbus; end end endalways @(posedge clk) begin if (reset) begin trisc <= 8'hff; // Default is to tristate them end else begin if (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//// Seperate the PC_IN input bus into PC from the sequential register so that we// can feed the PC_IN bus into the PRAM address input.always @(posedge clk) if (reset) pc <= RESET_VECTOR; else pc <= pc_in;always @(inst or stacklevel or status or stack1 or stack2 or pc or dbus) begin casex ({inst, stacklevel}) // synopsys parallel_case 14'b101?_????_????_??: pc_in = {status[6:5], inst[8:0]}; // GOTO 14'b1001_????_????_??: pc_in = {status[6:5], 1'b0, inst[7:0]}; // CALL 14'b1000_????_????_00: pc_in = stack1; // RETLW 14'b1000_????_????_01: pc_in = stack1; // RETLW 14'b1000_????_????_10: pc_in = stack2; // RETLW 14'b1000_????_????_11: pc_in = stack2; // RETLW 14'b00?0_0010_0010_??: pc_in = {pc[10:8], dbus}; // MOVF or MOVWF where f=PC default: pc_in = pc + 1; endcaseend// 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 // CALL instruction if (inst[11:8] == 4'b1001) begin case (stacklevel) // synopsys parallel_case 2'b00: // No previous CALLs begin stack1 <= pc; end 2'b01: // ONE previous CALL begin stack2 <= 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 endend// 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 casex ({inst, stacklevel}) // synopsys parallel_case // 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 endend// ******* Debug Stuff ******** ////// The following is NOT synthesizable. This code simply allows you to see the ASCII name// for the instruction in the INST register while in a waveform viewer.//// synopsys translate_offreg [8*8-1:0] inst_string;always @(inst) begin casex (inst) 12'b0000_0000_0000: inst_string = "NOP "; 12'b0000_001X_XXXX: inst_string = "MOVWF "; 12'b0000_0100_0000: inst_string = "CLRW "; 12'b0000_011X_XXXX: inst_string = "CLRF "; 12'b0000_10XX_XXXX: inst_string = "SUBWF "; 12'b0000_11XX_XXXX: inst_string = "DECF "; 12'b0001_00XX_XXXX: inst_string = "IORWF "; 12'b0001_01XX_XXXX: inst_string = "ANDWF "; 12'b0001_10XX_XXXX: inst_string = "XORWF "; 12'b0001_11XX_XXXX: inst_string = "ADDWF "; 12'b0010_00XX_XXXX: inst_string = "MOVF "; 12'b0010_01XX_XXXX: inst_string = "COMF "; 12'b0010_10XX_XXXX: inst_string = "INCF "; 12'b0010_11XX_XXXX: inst_string = "DECFSZ "; 12'b0011_00XX_XXXX: inst_string = "RRF "; 12'b0011_01XX_XXXX: inst_string = "RLF "; 12'b0011_10XX_XXXX: inst_string = "SWAPF "; 12'b0011_11XX_XXXX: inst_string = "INCFSZ "; // *** Bit-Oriented File Register Operations 12'b0100_XXXX_XXXX: inst_string = "BCF "; 12'b0101_XXXX_XXXX: inst_string = "BSF "; 12'b0110_XXXX_XXXX: inst_string = "BTFSC "; 12'b0111_XXXX_XXXX: inst_string = "BTFSS "; // *** Literal and Control Operations 12'b0000_0000_0010: inst_string = "OPTION "; 12'b0000_0000_0011: inst_string = "SLEEP "; 12'b0000_0000_0100: inst_string = "CLRWDT "; 12'b0000_0000_0101: inst_string = "TRIS "; 12'b0000_0000_0110: inst_string = "TRIS "; 12'b0000_0000_0111: inst_string = "TRIS "; 12'b1000_XXXX_XXXX: inst_string = "RETLW "; 12'b1001_XXXX_XXXX: inst_string = "CALL "; 12'b101X_XXXX_XXXX: inst_string = "GOTO "; 12'b1100_XXXX_XXXX: inst_string = "MOVLW "; 12'b1101_XXXX_XXXX: inst_string = "IORLW "; 12'b1110_XXXX_XXXX: inst_string = "ANDLW "; 12'b1111_XXXX_XXXX: inst_string = "XORLW "; default: inst_string = "-XXXXXX-"; endcaseend // synopsys translate_onendmodule
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -