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;