pc_control.vhd


This vhdl code synthesizes the PC block of the DLX processor

S1bus

This is a tristate output bus driven by the PC, Instruction address register(IAR) and the temperory Instruction Register (IRtemp) in this PC block

S2bus

Tristate output bus driven by the IRtemp in this block

Instrbus

Input to the Instruction Register(IR)

IRload

Load enable line of the IR

IRmux

Multiplexer Select line that selects the input to IR

IR_rs1

Contains the address of the register in which source1 is

IR_rs2

Contains the address of the register in which source 2 is

IR_rsd

Contains the address of the destination register

opcode

Contains the 6-bit opcode of the instruction

opcodeALU

Contains the 6-bit ALU opcode

IRTEMPoeS1

Output enable of the IRTEMP register to output the value to the S1bus

IRTEMPoeS2

Output enble of the IRTEMP register to output the value to the S2bus

IAR_oe

Output enable of the IAR

PCload

Load enable of the PC register

PCmux

Mux select line that selects the input to the PC register
The inputs to the multiplexer are output of the adder, Destbus, Destbus_a, Destbus_b, LMDRbus, Aregbus
@ ************* Model Documentation End ************************************* pc_control.vhd - synthesizeable version library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; library synopsys; use synopsys.attributes.all; use work.define.all; instruction memory interface and associated registers contains code for PC, IR, IRtemp, and saved PC values adder for PC is in another module Also has bus interface logic for memory bus. This is simpler than the data memory BIU because read only, all fetches are aligned. Basically just the wait state handler logic entity pc_control is port( system clock IN std_logic; clock2x IN std_logic; Reset IN std_logic; MemStall IN std_logic; S1bus OUT std_logic_vector(31 downto 0); S2bus OUT std_logic_vector(31 downto 0); ConstantSelect IN std_logic; selects a constant value to be gated to S2 bus Instrbus IN std_logic_vector(31 downto 0); instruction bus,input to IR IRload IN std_logic; load line for instruction reg IRmux IN std_logic_vector(1 downto 0); IR_rs1 OUT std_logic_vector(4 downto 0); rs1 source IR_rs2 OUT std_logic_vector(4 downto 0); rs2 source IR_rsd OUT std_logic_vector(4 downto 0); rsd source opcode OUT std_logic_vector(5 downto 0); opcodeALU OUT std_logic_vector(5 downto 0); IRtemp control,holds immediate and constant values IRTEMPoeS1 IN std_logic; IRTEMPoeS2 IN std_logic; sxt_imm IN std_logic; controls sign extension of the immediate value IAR irq_interrupt IN std_logic; clr_globalmsk OUT std_logic; iar_oe in std_logic; PC, PCMUX and PC adder unit PCload IN std_logic; Iaddrbus OUT std_logic_vector(31 downto 0); instruction address bus PCmux IN std_logic_vector(3 downto 0); controls source for PC load Destbus IN std_logic_vector(31 downto 0); output of ALU Destbus_a IN std_logic_vector(31 downto 0); previous value of Destbus Destbus_b IN std_logic_vector(31 downto 0); 2nd previous value of Destbus Aregbus IN std_logic_vector(31 downto 0); output of 'A' side of register file pcadd_b OUT std_logic_vector(31 downto 0); B operand for adder, A operand is Iaddrbus pcadd_ci OUT std_logic; CI operand for adder pcadd_sum IN std_logic_vector(31 downto 0); adder sum LMDRbus in std_logic_vector(31 downto 0); output of LMDR register, needed by PC Instruction Memory Control NoIMemStart in std_logic; IMemStart_L out std_logic; indicates start of memory cycle IMemWait_in IN std_logic; wait signal from Instruction Memory IMemStall OUT std_logic; wait signal to rest of DLX NoWrite_MEM out std_logic; inhibit write in mem stage NoWrite_WB out std_logic inhibit write in WB stage ); end pc_control; architecture behv of pc_control is signal nstate_pc,pstate_pc std_logic_vector(31 downto 0); pc signal nstate_ir,pstate_ir std_logic_vector(31 downto 0); ir signal nstate_iar,pstate_iar std_logic_vector(31 downto 0); iar signal nstate_irtemp,pstate_irtemp std_logic_vector(31 downto 0); irtemp signal pstate_IMemStart_L, nstate_IMemStart_L std_logic; signal pstate_ResetStart, nstate_ResetStart std_logic; signal nstate_IMemWait_out,pstate_IMemWait_out std_logic; pc dec, pc exe, pc mem, saved PC for interrupts signal nstate_pcdec,pstate_pcdec std_logic_vector(31 downto 0); signal nstate_pcexe,pstate_pcexe std_logic_vector(31 downto 0); signal nstate_pcmem,pstate_pcmem std_logic_vector(31 downto 0); flags for ihibiting write in case of interrupts signal nstate_nowrite_dec,pstate_nowrite_dec std_logic; signal nstate_nowrite_exe,pstate_nowrite_exe std_logic; signal nstate_nowrite_mem,pstate_nowrite_mem std_logic; signal nstate_nowrite_wb,pstate_nowrite_wb std_logic; signal pc_spec_src std_logic_vector(31 downto 0); begin S1 bus only time constantSelect = '1' is when we are trying to use the ALU to compute a return address S1bus <= pstate_pcexe when (ConstantSelect = '1') else pstate_iar when (IAR_oe = '1') else pstate_irtemp when (IRTEMPoeS1 = '1') else (others => 'Z'); S2 bus first case is used to increment PC by 8, needed by JALR, JAL S2bus <= (3 => '1', OTHERS=> '0') when (ConstantSelect = '1') else pstate_irtemp when (IRTEMPoeS2 = '1') else (others => 'Z'); IR normally loaded from data memory, but in the case interrupts the fetch stage will force in a trap instruction which causes a jump to the vector location for that trap
ir_input process (Instrbus, irq_interrupt,irmux) begin if (irq_interrupt = '1') then nstate_ir <= IRQ_VEC; else case irmux is when IRQ_TRAP => nstate_ir <= IRQ_VEC; when IR_NOP => nstate_ir <= NOP; when others => nstate_ir <= Instrbus; end case; end if; end process ir_input; IR_rs1 <= pstate_ir(25 downto 21); also 'Aaddr' value IR_rs2 <= pstate_ir(20 downto 16); also 'Baddr' value IR_rsd <= pstate_ir(15 downto 11); opcode <= pstate_ir(31 downto 26); opcodeALU <= pstate_ir(5 downto 0); ir_state process(Reset,IRload,clock,MemStall) begin if (Reset = '1') then pstate_ir <= (others => '0'); elsif (IRload = '1' and MemStall = '0') then if (clock'event and clock = '0') then pstate_ir <= nstate_ir; end if; end if; end process ir_state; IRTEMP irtemp_comb process(sxt_imm,pstate_ir) begin IRTEMP will usually have the sign-extended immediate value of instruction in the instruction register, only unsigned immediate ALU instructions will get the zero extended value nstate_irtemp(15 downto 0) <= pstate_ir(15 downto 0); if ((pstate_ir(15) = '0') or (sxt_imm = '0')) then i = i & 0x0000ffff; nstate_irtemp(31 downto 16) <= "0000000000000000"; else i = i | 0xffff0000; nstate_irtemp(31 downto 16) <= "1111111111111111"; end if; end process irtemp_comb; irtemp also loaded by IRload. Gets a new value every time ir does
irtemp_state process(Reset,IRload,clock,MemStall) begin if (Reset = '1') then pstate_irtemp <= (others => '0'); elsif (IRload = '1' and MemStall='0') then if (clock'event and clock = '0') then pstate_irtemp <= nstate_irtemp; end if; end if; end process irtemp_state; IAR nstate_iar <= pstate_pcmem; only input for iar is pc of mem stage iar_state process(Reset,clock,irq_interrupt) begin if (Reset = '1') then pstate_iar <= (others => '0'); elsif (irq_interrupt = '1') then if (clock = '0' and clock'event) then pstate_iar <= nstate_iar; end if; end if; end process iar_state; currently, there is only type of interrupt, so the PC special source is always the IAR when we add other interrupt types such as software execeptions like undefined opcodes, these will have their own IARs and pc_spec_src will have to decoded from the 'rs1' field pc_spec_src <= pstate_iar; write inhibit flags NoWrite_MEM <= pstate_nowrite_mem; NoWrite_WB <= pstate_nowrite_wb; pcexe - PC of instruction in execute stage pcmem - PC of instruction in mem stage nstate_pcexe <= pstate_pcdec; only one source for PCexe nstate_pcmem <= pstate_pcexe; only one source for PCmem these get loaded every clock except when MEMstall occurs even when local stalls occur, it will be ok pctemp_state process(clock,reset,MemStall) begin if (Reset = '1') then pstate_pcexe <= (others => '0'); pstate_pcmem <= (others => '0'); pstate_nowrite_exe <= '0'; pstate_nowrite_mem <= '0'; pstate_nowrite_wb <= '0'; elsif (MemStall='0') then if (clock'event and clock = '0') then pstate_pcexe <= nstate_pcexe; pstate_pcmem <= nstate_pcmem; pstate_nowrite_exe <= nstate_nowrite_exe; pstate_nowrite_mem <= nstate_nowrite_mem; pstate_nowrite_wb <= nstate_nowrite_wb; end if; end if; end process pctemp_state; force to '1' when an interrupt is recognized nstate_nowrite_exe <= '1' when (irq_interrupt = '1') else pstate_nowrite_dec; nstate_nowrite_mem <= '1' when (irq_interrupt ='1') else pstate_nowrite_exe; nstate_nowrite_wb <= '1' when (irq_interrupt = '1') else pstate_nowrite_mem; these flags are loaded the same way the PC exe, mem registers are loaded
pcnowriteb_state process(clock,reset,MemStall,irq_interrupt) begin if (Reset = '1') then pstate_nowrite_exe <= '0'; pstate_nowrite_mem <= '0'; pstate_nowrite_wb <= '0'; elsif (MemStall='0' or irq_interrupt = '1') then if (clock'event and clock = '0') then pstate_nowrite_exe <= nstate_nowrite_exe; pstate_nowrite_mem <= nstate_nowrite_mem; pstate_nowrite_wb <= nstate_nowrite_wb; end if; end if; end process pcnowriteb_state; PC Iaddrbus <= pstate_pc; pc_comb process(pstate_pc,pstate_ir,PCmux, Destbus, Destbus_a, Destbus_b,Aregbus,LMDRbus, pcadd_sum,pc_spec_src) variable ii std_logic_vector(31 downto 0); begin PC combinational logic pcadd_ci <= '0'; clr_globalmsk <= '0'; adder 'b' side mux for PC addition CASE PCmux is WHEN PC_add16=> add 16 bit immediate from IR register ii(15 downto 0) = pstate_ir(15 downto 0); if (pstate_ir(15) = '0') then i = i & 0x0000ffff; ii(31 downto 16) = "0000000000000000"; else i = i | 0xffff0000; else ii(31 downto 16) = "1111111111111111"; end if; pcadd_b <= ii; WHEN PC_add26=> add 26 bit immediate from IR register ii(25 downto 0) = pstate_ir(25 downto 0); if (pstate_ir(25) = '0') then i = i & 0x03ffffff; ii(31 downto 26) = "000000"; else i = i | 0xfc000000; else ii(31 downto 26)= "111111"; end if; pcadd_b <= ii; WHEN OTHERS=> default is always add 4 pcadd_b <= (2=>'1', OTHERS => '0'); END CASE; nstate_pc (input to PC register) CASE PCmux is WHEN PC_const16=> nstate_pc <= (OTHERS => '0'); nstate_pc(4) <= '1'; WHEN PC_destbus=> nstate_pc <= Destbus; WHEN PC_destbus_a=> nstate_pc <= Destbus_a; WHEN PC_destbus_b=> nstate_pc <= Destbus_b; WHEN PC_Aregbus=> nstate_pc <= Aregbus; WHEN PC_LMDR=> nstate_pc <= LMDRbus; WHEN PC_TRAP=> ii(25 downto 0) = pstate_ir(25 downto 0); if (pstate_ir(25) = '0') then i = i & 0x03ffffff; ii(31 downto 26) = "000000"; else i = i | 0xfc000000; else ii(31 downto 26)= "111111"; end if; PC = i; nstate_pc <= ii; WHEN PC_SPECIAL => - this is reserved for loading the PC from a return from interrupt source. Only one currently (the IRQ sources), lets clear the global interrupt mask as well as loading the PC clr_globalmsk <= '1'; nstate_pc <= pc_spec_src; WHEN OTHERS=> default is output of adder nstate_pc <= pcadd_sum; END CASE; end process pc_comb; pcdec - PC of instruction in decode stage nstate_pcdec <= pstate_pc; only one source for PCdec new pc is loaded, old pc is passed to decode stage pc_state process(Reset,PCload,clock,MemStall) begin if (Reset = '1') then pstate_pc <= (others => '0'); pstate_pcdec <= (others => '0'); elsif (PCload = '1' and MemStall='0') then if (clock'event and clock = '0') then pstate_pc <= nstate_pc; pstate_pcdec <= nstate_pcdec; end if; end if; end process pc_state; this flag follows the state of the PC's for the decode state these flags are normally loaded when the of decode PC's change
the Fetch stage does not need one because when we get an IRQ, a trap instruction is forced into the pipeline at the fetch stage nstate_nowrite_dec <= '1' when (irq_interrupt = '1') else '0'; pcnowritea_state process(clock,reset,PCload,irq_interrupt) begin if (Reset = '1') then pstate_nowrite_dec <= '0'; elsif (PCload='1' or irq_interrupt = '1') then if (clock'event and clock = '0') then pstate_nowrite_dec <= nstate_nowrite_dec; end if; end if; end process pcnowritea_state; IMemStart_L indicates start of memory cycle - if this line is tied back to IMemWait_in then memory is synchronous the ResetStart state signal is used to pulse the reset line on the IMemStart flip-flop, this is clocked by the clock2x signal on falling edge ResetStart_comb process(pstate_ResetStart,pstate_IMemStart_L,clock,NoIMemStart) begin nstate_ResetStart <= '0'; reset every clock edge we are trying to start the memory cycle if ( (pstate_ResetStart = '0' and NoIMemStart = '0') or we are in the memory cycle already and want to make sure it keeps going ( (pstate_IMemStart_L = '0') and (clock = '0'))) then nstate_ResetStart <= '1'; end if; end process ResetStart_comb; ResetStart_state process (clock2x,reset) begin if (Reset = '1') then pstate_ResetStart <= '0'; else if (clock2x'event and clock2x='0') then pstate_ResetStart <= nstate_ResetStart; end if; end if; end process ResetStart_state; IMemStart_L <= pstate_IMemStart_L; force low at least once a cycle when clock = '0' and clock2x = '1' unless we are in a wait state this configuration should not glitch, the extra logic on the reset line is driven directly by a flip-flop output IMemStart_L state clock on rising edge of clock2x set it back if currently reset and not waiting nstate_IMemStart_L <= '1' when ( (pstate_IMemWait_out = '0') and (pstate_IMemStart_L = '0')) else pstate_IMemStart_L; IMemStart_state process (Reset,pstate_ResetStart,clock2x) begin if ((Reset = '1') or (pstate_ResetStart = '1')) then pstate_IMemStart_L <= '0'; else if (clock2x'event and clock2x = '1') then pstate_IMemStart_L <= nstate_IMemStart_L; end if; end if; end process IMemStart_state; latching of wait signal from memory IMemStall <= pstate_IMemWait_out; nstate_IMemWait_out <= IMemWait_in; sampled on rising edge of main clock IMemWait_out_state process(Reset,clock) begin if (Reset = '1') then pstate_IMemWait_out <= '0'; else if (clock'event and clock = '1') then pstate_IMemWait_out <= nstate_IMemWait_out; end if; end if; end process IMemWait_out_state; end behv;