📄 cpu.vhd
字号:
else
bd <= bdec;
end if;
end process;
p_inst : process(CLK,RESET)
begin
if (RESET = '1') then
inst <= x"000";
elsif CLK'event and (CLK = '1') then
if (skip = '1') then
inst <= x"000"; -- force NOP
else
inst <= PDATA;
end if;
end if;
end process;
p_skip_comb : process(inst,alu_z,fwe,special_sel,fileaddr)
begin
-- SKIP signal.
-- We want to insert the NOP instruction for the following conditions:
-- we have modified PCL
-- GOTO,CALL and RETLW instructions
-- BTFSS instruction when aluz is HI
-- BTFSC instruction when aluz is LO
skip <= '0';
if (fwe = '1') and (special_sel = '1') and (fileaddr(2 downto 0) = PCL_ADDR) then skip <= '1'; end if;
if (inst(11 downto 10) = "10") then skip <= '1'; end if;
if (inst(11 downto 8) = "0110") and (alu_z = '1') then skip <= '1'; end if; -- BTFSC
if (inst(11 downto 8) = "0111") and (alu_z = '0') then skip <= '1'; end if; -- BTFSS
if (inst(11 downto 6) = "001011") and (alu_z = '1') then skip <= '1'; end if; -- DECFSZ
if (inst(11 downto 6) = "001111") and (alu_z = '1') then skip <= '1'; end if; -- INCFSZ
end process;
sbus_swap <= sbus(3 downto 0) & sbus(7 downto 4);
alua_mux : MUX4
generic map (
WIDTH => 8,
SLICE => 1,
OP_REG => FALSE
)
port map (
DIN3 => sbus_swap,
DIN2 => inst_k,
DIN1 => sbus,
DIN0 => w_reg,
SEL => alu_asel,
ENA => '0',
CLK => '0',
DOUT => alu_a
);
alub_mux : MUX4
generic map (
WIDTH => 8,
SLICE => 0,
OP_REG => FALSE
)
port map (
DIN3 => x"01",
DIN2 => bd,
DIN1 => sbus,
DIN0 => w_reg,
SEL => alu_bsel,
ENA => '0',
CLK => '0',
DOUT => alu_b
);
p_w_reg : process(CLK,RESET)
begin
if (RESET = '1') then
w_reg <= x"00";
elsif CLK'event and (CLK = '1') then
if (wwe = '1') then
w_reg <= dbus;
end if;
end if;
end process;
p_tmr0 : process(CLK,RESET)
variable mask : std_logic_vector(7 downto 0);
begin
if (RESET = '1') then
tmr0 <= x"00";
elsif CLK'event and (CLK = '1') then
-- See if the timer register is actually being written to
if (fwe = '1') and (special_sel = '1') and (fileaddr(2 downto 0) = TMR0_ADDR) then
tmr0 <= dbus;
else
mask := "00000001";
case option(2 downto 0) is
when "000" => mask := "00000001";
when "001" => mask := "00000011";
when "010" => mask := "00000111";
when "011" => mask := "00001111";
when "100" => mask := "00011111";
when "101" => mask := "00111111";
when "110" => mask := "01111111";
when "111" => mask := "11111111";
when others => null;
end case;
if ((prescaler and mask) = "00000000") or (option(3) = '1') then
tmr0 <= tmr0 + "1";
end if;
end if;
end if;
end process;
p_prescaler : process(CLK,RESET)
begin
if (RESET = '1') then
prescaler <= x"00";
elsif CLK'event and (CLK = '1') then
if not (option(5) = '1') then
prescaler <= prescaler + "1";
end if;
end if;
end process;
p_status_reg : process(CLK,RESET)
variable new_z,new_dc,new_c : std_logic;
begin
if (RESET = '1') then
status <= STATUS_RESET_VALUE;
elsif CLK'event and (CLK = '1') then
-- See if the status register is actually being written to
-- this is not accurate, bits 4 & 3 should be read only
-- additionally, zwe,cwe and dcwe should override fwe
if (fwe = '1') and (special_sel = '1') and (fileaddr(2 downto 0) = STATUS_ADDR) then
status <= dbus;
else
-- 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.
if (zwe = '1') then new_z := alu_z; else new_z := status(2); end if;
if (dcwe = '1') then new_dc := alu_dcout; else new_dc := status(1); end if;
if (cwe = '1') then new_c := alu_cout; else new_c := status(0); end if;
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)
new_z & -- BIT 2: Z
new_dc & -- BIT 1: DC
new_c); -- BIT 0: C
end if;
end if;
end process;
p_fsr_reg : process(CLK,RESET)
begin
if (RESET = '1') then
fsr <= x"80";
elsif CLK'event and (CLK = '1') then
if (fwe = '1') and (special_sel = '1') and (fileaddr(2 downto 0) = FSR_ADDR) then
fsr <= dbus;
end if;
fsr(7) <= '1'; --always set in real chip
end if;
end process;
p_option_reg : process(CLK,RESET)
begin
if (RESET = '1') then
option <= OPTION_RESET_VALUE;
elsif CLK'event and (CLK = '1') then
if (isoption = '1') then
option <= dbus;
end if;
end if;
end process;
p_drive_ports_comb : process(porta_dout,trisa,portb_dout,trisb,portc_dout,trisc)
begin
PORTA_OE_L <= trisa;
PORTB_OE_L <= trisb;
PORTC_OE_L <= trisc;
PORTA_OUT <= porta_dout;
PORTB_OUT <= portb_dout;
PORTC_OUT <= portc_dout;
end process;
port_in : process(CLK,RESET,PORTA_IN,PORTB_IN,PORTC_IN)
begin
-- the input registers don't exist in the real device,
-- so if you read an output we have introduced a clock delay.
if (RESET = '1') then
porta_din <= (others => '0');
portb_din <= (others => '0');
portc_din <= (others => '0');
elsif CLK'event and (CLK = '1') then -- comment this out for combinatorial ip
--else -- or comment this for registered ip
porta_din <= PORTA_IN;
portb_din <= PORTB_IN;
portc_din <= PORTC_IN;
end if;
end process;
p_port_reg : process(CLK,RESET)
begin
if (RESET = '1') then
trisa <= x"FF"; -- default tristate
trisb <= x"FF"; -- default tristate
trisc <= x"FF"; -- default tristate
porta_dout <= x"00";
portb_dout <= x"00";
portc_dout <= x"00";
elsif CLK'event and (CLK = '1') then
if (fwe = '1') and (fileaddr(2 downto 0) = PORTA_ADDR) then
if (istris = '0') and (special_sel = '1') then
porta_dout <= dbus;
elsif (istris = '1') then
trisa <= dbus;
end if;
end if;
if (fwe = '1') and (fileaddr(2 downto 0) = PORTB_ADDR) then
if (istris = '0') and (special_sel = '1') then
portb_dout <= dbus;
elsif (istris = '1') then
trisb <= dbus;
end if;
end if;
if (fwe = '1') and (fileaddr(2 downto 0) = PORTC_ADDR) then
if (istris = '0') and (special_sel = '1') then
portc_dout <= dbus;
elsif (istris = '1') then
trisc <= dbus;
end if;
end if;
end if;
end process;
-- ********** PC AND STACK *************************
p_next_pc_comb : process(pc,inst,status,stacklevel,stack1,stack2,dbus,fileaddr,special_sel,fwe)
begin
pc_goto <= ( status(6 downto 5) & inst(8 downto 0));
pc_call <= ( status(6 downto 5) & '0' & inst(7 downto 0));
pc_write <= (pc(10) & '0' & pc(8) & dbus); -- set bit 9 to zero
pc_inc <= '1'; -- default
pc_load_sel <= "00"; -- pc write
if (fwe = '1') and (special_sel = '1') and (fileaddr(2 downto 0) = PCL_ADDR) then
--pc_load_sel <= "00"; default
pc_inc <= '0'; -- as we have modified next_pc, must skip next instruction
end if;
if (inst(11 downto 9) = "101") then pc_load_sel <= "01"; pc_inc <= '0'; end if; -- goto
if (inst(11 downto 8) = "1001") then pc_load_sel <= "10"; pc_inc <= '0'; end if; -- call
if (inst(11 downto 8) = "1000") then pc_load_sel <= "11"; pc_inc <= '0'; end if; -- ret
end process;
pc_load_mux : MUX4
generic map (
WIDTH => 11,
SLICE => 0,
OP_REG => FALSE
)
port map (
DIN3 => pc_load_stack,
DIN2 => pc_call,
DIN1 => pc_goto,
DIN0 => pc_write,
SEL => pc_load_sel,
ENA => '0',
CLK => '0',
DOUT => pc_load
);
pc_mux2_add_reg : MUX2_ADD_REG
generic map (
WIDTH => 11
)
port map (
ADD_VAL => "00000000001", -- pc = pc + 1
LOAD_VAL => pc_load, -- branch
ADD => pc_inc,
PRESET => RESET,
ENA => '1',
CLK => CLK,
DOUT => next_pc,
REG_DOUT => pc
);
p_stack_comb : process(stacklevel,stack1,stack2)
begin
pc_load_stack <= stack1; -- default
case stacklevel is
when "00" => pc_load_stack <= stack1;
when "01" => pc_load_stack <= stack1;
when "10" => pc_load_stack <= stack2;
when "11" => pc_load_stack <= stack2;
when others => null;
end case;
end process;
p_stack_reg : process(CLK,RESET)
begin
if (RESET = '1') then
stack1 <= (others => '0');
stack2 <= (others => '0');
elsif CLK'event and (CLK = '1') then
if (inst(11 downto 8) = "1001") then
case stacklevel is
when "00" => stack1 <= pc(10 downto 0);
when "01" => stack2 <= pc(10 downto 0);
when "10" => assert false report "Too many CALLs !" severity failure;
when "11" => assert false report "Too many CALLs !" severity failure;
when others => null;
end case;
end if;
end if;
end process;
p_stack_level : process(CLK,RESET)
begin
if (RESET = '1') then
stacklevel <= "00";
elsif CLK'event and (CLK = '1') then
stacklevel <= stacklevel;
if (inst(11 downto 8) = "1001") then
case stacklevel is
when "00" => stacklevel <="01"; -- 1st call
when "01" => stacklevel <="10"; -- 2nd call
when "10" => stacklevel <="10"; -- already 2, ignore
when "11" => stacklevel <="00"; -- broke
when others => null;
end case;
elsif (inst(11 downto 8) = "1000") then
case stacklevel is
when "00" => stacklevel <="00"; -- broke
when "01" => stacklevel <="00"; -- go back to no call
when "10" => stacklevel <="01"; -- go back to 1 call
when "11" => stacklevel <="10"; -- broke
when others => null;
end case;
end if;
end if;
end process;
end rtl;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -