📄 piccpu.vhd
字号:
OPCODE_XORWF <= '1' when INST(11 downto 6) = XORWF else '0';
OPCODE_ADDWF <= '1' when INST(11 downto 6) = ADDWF else '0';
OPCODE_IORWF <= '1' when INST(11 downto 6) = IORWF else '0';
OPCODE_MOVF <= '1' when INST(11 downto 6) = MOVF else '0';
OPCODE_COMF <= '1' when INST(11 downto 6) = COMF else '0';
OPCODE_INCF <= '1' when INST(11 downto 6) = INCF else '0';
OPCODE_DECFSZ <= '1' when INST(11 downto 6) = DECFSZ else '0';
OPCODE_RRF <= '1' when INST(11 downto 6) = RRF else '0';
OPCODE_RLF <= '1' when INST(11 downto 6) = RLF else '0';
OPCODE_SWAPF <= '1' when INST(11 downto 6) = SWAPF else '0';
OPCODE_INCFSZ <= '1' when INST(11 downto 6) = INCFSZ else '0';
-- Bit-Oriented File Register Operations
OPCODE_BCF <= '1' when INST(11 downto 8) = BCF else '0';
OPCODE_BSF <= '1' when INST(11 downto 8) = BSF else '0';
OPCODE_BTFSC <= '1' when INST(11 downto 8) = BTFSC else '0';
OPCODE_BTFSS <= '1' when INST(11 downto 8) = BTFSS else '0';
-- Literal and Control Operations
OPCODE_OPTION <= '1' when INST(11 downto 0) = OPTION else '0';
OPCODE_SLEEP <= '1' when INST(11 downto 0) = SLEEP else '0';
OPCODE_CLRWDT <= '1' when INST(11 downto 0) = CLRWDT else '0';
--*** CHECK OUT THE TRIS INSTRUCTION
OPCODE_TRIS <= '1' when INST(11 downto 0) = TRIS else '0';
OPCODE_RETLW <= '1' when INST(11 downto 8) = RETLW else '0';
OPCODE_CALL <= '1' when INST(11 downto 8) = CALL else '0';
OPCODE_GOTO <= '1' when INST(11 downto 9) = GOTO else '0';
OPCODE_MOVLW <= '1' when INST(11 downto 8) = MOVLW else '0';
OPCODE_IORLW <= '1' when INST(11 downto 8) = IORLW else '0';
OPCODE_ANDLW <= '1' when INST(11 downto 8) = ANDLW else '0';
OPCODE_XORLW <= '1' when INST(11 downto 8) = XORLW else '0';
-- So, look at the instruction chart. Which instructions affect the Z flag
--
STATUS_Z_WRITE <= '1' when ((OPCODE_CLRW = '1') OR
(OPCODE_CLRF = '1') OR
(OPCODE_SUBWF = '1') OR
(OPCODE_DECF = '1') OR
(OPCODE_IORWF = '1') OR
(OPCODE_ANDWF = '1') OR
(OPCODE_XORWF = '1') OR
(OPCODE_ADDWF = '1') OR
(OPCODE_MOVF = '1') OR
(OPCODE_COMF = '1') OR
(OPCODE_INCF = '1') OR
(OPCODE_IORLW = '1') OR
(OPCODE_ANDLW = '1') OR
(OPCODE_XORLW = '1')) Else '0';
-- So, look at the instruction chart. Which instructions affect the C flag
--
STATUS_C_WRITE <= '1' when ((OPCODE_SUBWF = '1') OR
(OPCODE_ADDWF = '1') OR
(OPCODE_RRF = '1') OR
(OPCODE_RLF = '1')) Else '0';
-- The input to the W register is WIN and is fed by a mux.
-- There are only two busses that can affect a new W value; the W
-- register itself, or the ALU output. Now, even though there is
-- a MOVLW instruction, the literal travels through the ALU!
--
WIN <= ALUOUT when ( (OPCODE_CLRW = '1') OR
(OPCODE_SUBWF = '1' AND D = '0') OR
(OPCODE_DECF = '1' AND D = '0') OR
(OPCODE_IORWF = '1' AND D = '0') OR
(OPCODE_ANDWF = '1' AND D = '0') OR
(OPCODE_XORWF = '1' AND D = '0') OR
(OPCODE_ADDWF = '1' AND D = '0') OR
(OPCODE_MOVF = '1' AND D = '0') OR
(OPCODE_COMF = '1' AND D = '0') OR
(OPCODE_INCF = '1' AND D = '0') OR
(OPCODE_DECFSZ = '1' AND D = '0') OR
(OPCODE_RRF = '1' AND D = '0') OR
(OPCODE_RLF = '1' AND D = '0') OR
(OPCODE_SWAPF = '1' AND D = '0') OR
(OPCODE_INCFSZ = '1' AND D = '0') OR
(OPCODE_RETLW = '1') OR
(OPCODE_MOVLW = '1') OR
(OPCODE_IORLW = '1') OR
(OPCODE_ANDLW = '1') OR
(OPCODE_XORLW = '1'))
else W;
-- The input to the Register File is FIN and is fed by a mux.
-- The only two sources for new Register File values is either
-- the register file itself (in other words, no change) or the
-- output of the ALU. Remember! Most data gets routed through
-- the ALU even if the ALU is not really doing anything to it.
-- Look at the ALUOP operations per instruction to really understand
-- this.
--
FIN <= ALUOUT when ( (OPCODE_CLRF = '1') OR
(OPCODE_MOVWF = '1') OR
(OPCODE_SUBWF = '1' AND D = '1') OR
(OPCODE_DECF = '1' AND D = '1') OR
(OPCODE_IORWF = '1' AND D = '1') OR
(OPCODE_ANDWF = '1' AND D = '1') OR
(OPCODE_XORWF = '1' AND D = '1') OR
(OPCODE_ADDWF = '1' AND D = '1') OR
(OPCODE_MOVF = '1' AND D = '1') OR
(OPCODE_COMF = '1' AND D = '1') OR
(OPCODE_INCF = '1' AND D = '1') OR
(OPCODE_DECFSZ = '1' AND D = '1') OR
(OPCODE_RRF = '1' AND D = '1') OR
(OPCODE_RLF = '1' AND D = '1') OR
(OPCODE_SWAPF = '1' AND D = '1') OR
(OPCODE_BCF = '1') OR
(OPCODE_BSF = '1') OR
(OPCODE_INCFSZ = '1' AND D = '1'))
else FOUT;
-- Register File write enable depends on the instruction..
FWE <= '1' when ( (OPCODE_CLRF = '1') OR
(OPCODE_MOVWF = '1') OR
(OPCODE_BCF = '1') OR
(OPCODE_BSF = '1') OR
(OPCODE_SUBWF = '1' AND D = '1') OR
(OPCODE_DECF = '1' AND D = '1') OR
(OPCODE_IORWF = '1' AND D = '1') OR
(OPCODE_ANDWF = '1' AND D = '1') OR
(OPCODE_XORWF = '1' AND D = '1') OR
(OPCODE_ADDWF = '1' AND D = '1') OR
(OPCODE_MOVF = '1' AND D = '1') OR
(OPCODE_COMF = '1' AND D = '1') OR
(OPCODE_INCF = '1' AND D = '1') OR
(OPCODE_DECFSZ = '1' AND D = '1') OR
(OPCODE_RRF = '1' AND D = '1') OR
(OPCODE_RLF = '1' AND D = '1') OR
(OPCODE_SWAPF = '1' AND D = '1') OR
(OPCODE_INCFSZ = '1' AND D = '1'))
else '0';
-- The exact operation the ALU will perform is a function
-- of the instruction. Note that the ALU is used to do
-- things like copying and clearing registers. For example,
-- clearing a register can be accomplished by XORing any
-- two identical input into ALU and feeding the resulting
-- zero into the destination, therefore, the CLRW instruction
-- will force the ALU to do an XOR.
--
-- One way to see this, is to take the PIC instruction set table from
-- the data book, and add 3 new columns. First, the ALUA mux input
-- (which can be F, K, or W), second column is the ALUB mux input
-- (which can be F, K,, W or "00000001") and finally the third column
-- which is the ALUOP. For many instructions, the ALUOP is obvious.
-- For example, for the ADDWF instruction, the ALUOP value is ALU_ADD.
-- But for instruction like CLRW or MOVWF the ALUOP will be XOR, or OR.
-- A little bit of Boolean Algebra will help you through this.
--
-- I believe that the above mechanisms are likely what is actually going
-- on in the PIC, but since I'm not privy to their design, this is speculation.
--
ALUOP <= ALUOP_SUB when (OPCODE_SUBWF = '1') OR
(OPCODE_DECF = '1') OR
(OPCODE_DECFSZ = '1') else
ALUOP_ADD when (OPCODE_ADDWF = '1') OR
(OPCODE_INCF = '1') OR
(OPCODE_INCFSZ = '1') else
ALUOP_OR when (OPCODE_MOVWF = '1') OR
(OPCODE_IORWF = '1') OR
(OPCODE_MOVF = '1') OR
(OPCODE_RETLW = '1') OR
(OPCODE_MOVLW = '1') OR
(OPCODE_IORLW = '1') else
ALUOP_AND when (OPCODE_ANDWF = '1') OR
(OPCODE_ANDLW = '1') else
ALUOP_XOR when (OPCODE_CLRW = '1') OR
(OPCODE_CLRF = '1') OR
(OPCODE_XORWF = '1') OR
(OPCODE_XORLW = '1') else
ALUOP_COM when (OPCODE_COMF = '1') else
ALUOP_ROR when (OPCODE_RRF = '1') else
ALUOP_ROL when (OPCODE_RLF = '1') else
ALUOP_BITCLR when (OPCODE_BCF = '1') else
ALUOP_BITSET when (OPCODE_BSF = '1') else
ALUOP_BITTESTCLR when (OPCODE_BTFSC = '1') else
ALUOP_BITTESTSET when (OPCODE_BTFSS = '1') else
ALUOP_SWAP when (OPCODE_SWAPF = '1') else
"1111";
-- ALU Input port A should normally simply get the current W register.
--
ALUA <= FOUT when (OPCODE_SUBWF = '1') OR
(OPCODE_DECF = '1') OR
(OPCODE_MOVF = '1') OR
(OPCODE_COMF = '1') OR
(OPCODE_INCF = '1') OR
(OPCODE_DECFSZ = '1') OR
(OPCODE_RRF = '1') OR
(OPCODE_RLF = '1') OR
(OPCODE_SWAPF = '1') OR
(OPCODE_INCFSZ = '1') OR
(OPCODE_BCF = '1') OR
(OPCODE_BSF = '1') OR
(OPCODE_BTFSC = '1') OR
(OPCODE_BTFSS = '1') else
K when (OPCODE_USE_K_OPERAND = '1')
else W;
-- ALU Input port B should normally simply get the current F register file.
--
ALUB <= W when (OPCODE_MOVWF = '1') OR
(OPCODE_CLRW = '1') OR
(OPCODE_CLRF = '1') OR
(OPCODE_SUBWF = '1') OR
(OPCODE_IORLW = '1') OR
(OPCODE_ANDLW = '1') OR
(OPCODE_XORLW = '1') else
K when (OPCODE_BCF = '1') OR
(OPCODE_BSF = '1') OR
(OPCODE_BTFSC = '1') OR
(OPCODE_BTFSS = '1') OR
(OPCODE_RETLW = '1') OR
(OPCODE_MOVLW = '1') else
"00000001" when (OPCODE_DECF = '1') OR
(OPCODE_INCF = '1') OR
(OPCODE_DECFSZ = '1') OR
(OPCODE_INCFSZ = '1')
else FOUT;
-- Process for deriving our 4 phase clock from the main clock.
-- This is how the real PIC does it..
--
ClockDivider: process (Clk)
begin
-- *** Generate internal 4 phase clock
if Clk'EVENT AND Clk = '1' then
if MRST = '0' then
Q1 <= '1';
Q2 <= '0';
Q3 <= '0';
Q4 <= '0';
else
Q1 <= Q4;
Q2 <= Q1;
Q3 <= Q2;
Q4 <= Q3;
end if;
end if;
end process ClockDivider;
-- Main Process is based on rising edge of Q1.
--
MainProcess: process (Q1)
begin
if Q1'EVENT AND Q1 = '1' then
-- RESET!
if MRST = '0' then
-- Perhaps, more things need to be reset, but this is the minimum.
PC <= RESET_VECTOR;
INST <= NOP;
STATUS <= "00000000";
else
-- For 2 cycle instructions, latch a NOP into INST so as not to
-- distrurb ongoing instruction.
if (OPCODE_GOTO = '1') OR
(OPCODE_RETLW = '1') OR
(OPCODE_CALL = '1') OR
(OPCODE_DECFSZ = '1' AND ALUZ = '1') OR
(OPCODE_INCFSZ = '1' AND ALUZ = '1') OR
(OPCODE_BTFSC = '1' AND ALUZ = '1') OR
(OPCODE_BTFSS = '1' AND ALUZ = '1') OR
(FWE = '1' AND v1d2int(FSEL) = 2) then
-- NOP
INST <= NOP;
else
INST <= RomData;
end if;
-- Latch new values of registers based on MUX select lines
W <= WIN;
-- Handle writes to registers not actually in the REGFILE.
-- Special functions could be added here!
--
if FWE = '1' then
case v1d2int(FSEL) is
when 0 => NULL;
when 1 => RTCC <= FIN;
-- when 2 => PC(7 downto 0) <= FIN; *** handle this below ***
when 3 => STATUS <= FIN;
when 4 => FSR <= FIN;
-- when 5 => PortA <= FIN; -- Let's make PortA an input !
when 6 => PortBRegister <= FIN;
when 7 => PortCRegister <= FIN;
when OTHERS => NULL;
end case;
end if;
-- Handle Status Flags
if STATUS_C_WRITE = '1' then
STATUS(0) <= ALUCOUT;
end if;
if STATUS_Z_WRITE = '1' then
STATUS(2) <= ALUZ;
end if;
-- Handle the PC
--
-- GOTO instruction
if OPCODE_GOTO = '1' then
PC(8 downto 0) <= LONGK;
-- CALL instruction
elsif OPCODE_CALL = '1' then
PC(7 downto 0) <= K;
case v1d2int(STACKLEVEL) is
when 0 => STACK1 <= PC;
STACKLEVEL <= "01";
when 1 => STACK2 <= PC;
STACKLEVEL <= "10";
when OTHERS => NULL;
end case;
-- RETLW instruction
elsif OPCODE_RETLW = '1' then
-- You can add more stack levels here!
case v1d2int(STACKLEVEL) is
when 1 => PC <= STACK1;
STACKLEVEL <= "00";
when 2 => PC <= STACK2;
STACKLEVEL <= "01";
when OTHERS => NULL;
end case;
-- Handle direct write to PC
--
elsif FWE = '1' AND v1d2int(FSEL) = 2 then
PC(7 downto 0) <= FIN;
-- Otherwise, just increment the PC
else
PC <= PCPLUS1(10 downto 0);
end if;
end if;
end if;
end process;
-- *** NOT YET IMPLEMENTED ***
-- *** In general, modeling of Ports is minimal. ***
--
-- Process for sampling input ports based on rising edge of Q2
--SampleInputs: process (Q2)
--
--begin
-- if PRising(Q2) then
--
-- end if;
--end process SampleInputs;
end first;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -