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;