diff options
Diffstat (limited to 'zpu/hdl/zpu4/core')
-rw-r--r-- | zpu/hdl/zpu4/core/histogram.perl | 218 | ||||
-rw-r--r-- | zpu/hdl/zpu4/core/zpu_config.vhd | 58 | ||||
-rw-r--r-- | zpu/hdl/zpu4/core/zpu_core.vhd | 1014 | ||||
-rw-r--r-- | zpu/hdl/zpu4/core/zpu_core_small.vhd | 602 | ||||
-rw-r--r-- | zpu/hdl/zpu4/core/zpupkg.vhd | 218 |
5 files changed, 2110 insertions, 0 deletions
diff --git a/zpu/hdl/zpu4/core/histogram.perl b/zpu/hdl/zpu4/core/histogram.perl new file mode 100644 index 0000000..479ee0f --- /dev/null +++ b/zpu/hdl/zpu4/core/histogram.perl @@ -0,0 +1,218 @@ +#!/usr/bin/perl +############################################################################## +# +# Copyright (c) 2008 Salvador E. Tropea <salvador en inti gov ar> +# Copyright (c) 2008 Instituto Nacional de Tecnología Industrial +# +############################################################################## +# +# Target: Any +# Language: Perl +# Interpreter used: v5.6.1/v5.8.4 +# Text editor: SETEdit 0.5.5 +# +############################################################################## +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA +# +############################################################################## +# +# Description: Takes a ZPU trace and does some raw stats about opcodes +# frequency and speed. +# +############################################################################## +# +# TODO +# +# A lot ... +# + + +# 0x40-0x460 +# div y mod son especiales +@used=(); +@clks=(); + +$line=1; +$startLine=1; +#$endLine=10000; +$endLine=-1; +$lastClk=0; +$lastOpcode=-1; +while (<>) + { + if ($_=~/^(\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)/) + { + $clk=hex($7); + #print "$line\n"; + if ($line>=$startLine) + { + #print $_; + $addr=hex($1); + $opcode=hex($2); + $sp=hex($3); + $a=hex($4); + $b=hex($5); + if ($addr>=0x40 and $addr<0x460) + { + @used[$opcode+0x100]++; + @clks[$lastOpcodeEmu]+=$clk-$lastClkEmu unless lastOpcodeEmu==-1; + $lastOpcodeEmu=$opcode+0x100; + $lastClkEmu=$clk; + } + else + { + @used[$opcode]++; + @clks[$lastOpcode]+=$clk-$lastClk unless $lastOpcode==-1; + #printf "%d+=%d\n",$lastOpcode,$clk-$lastClk; + $lastOpcode=$opcode; + $lastClk=$clk; + $lastOpcodeEmu=-1; + $lastClkEmu=$clk; + } + } + else + { + $lastClk=$clk; + } + last if $line==$endLine; + $line++; + } + } +@used[$lastOpcode]--; + +$id=0; +# Cluster them +AddSimple('breakpoint',0); +# 1=shiftleft, invalid +AddSimple('pushsp',2); +# 3=pushint, invalid +AddSimple('poppc',4); +AddSimple('add',5); +AddSimple('and',6); +AddSimple('or',7); +AddSimple('load',8); +AddSimple('not',9); +AddSimple('flip',10); +AddSimple('nop',11); +AddSimple('store',12); +AddSimple('popsp',13); +# 14=compare, invalid +# 15=popint, invalid +AddSimpleRange('addsp',16,31); +# 32-63 emulate +AddSimpleRange('storesp',64,95); +AddSimpleRange('loadsp',96,127); +AddSimpleRange('im',128,255); + +# 32 is the reset entry point +# 33 is the interrupt entry point +AddEmulate('loadh',34); +AddEmulate('storeh',35); +AddEmulate('lessthan',36); +AddEmulate('lessthanorequal',37); +AddEmulate('ulessthan',38); +AddEmulate('ulessthanorequal',39); +AddEmulate('swap',40); # unimplemented +AddEmulate('mult',41); +AddEmulate('lshiftright',42); +AddEmulate('ashiftleft',43); +AddEmulate('ashiftright',44); +AddEmulate('call',45); +AddEmulate('eq',46); +AddEmulate('neq',47); +AddEmulate('neg',48); +AddEmulate('sub',49); +AddEmulate('xor',50); +AddEmulate('loadb',51); +AddEmulate('storeb',52); +AddEmulate('div',53); +AddEmulate('mod',54); +AddEmulate('eqbranch',55); +AddEmulate('neqbranch',56); +AddEmulate('poppcrel',57); +AddEmulate('config',58); +AddEmulate('pushpc',59); +AddEmulate('syscall_emulate',60); # unimplemented +AddEmulate('pushspadd',61); +AddEmulate('halfmult',62); # unimplemented +AddEmulate('callpcrel',63); + +$maxID=$id; +print "Total clocks: $lastClk\n"; +print "Unsorted:\n\n"; +for ($i=0; $i<$maxID; $i++) + { + $used=@used_noemu[$i]; + $clkm=0; + $clkm=@clks_noemu[$i]/$used if $used; + printf "%-20s %8d %6.2f\n",$names[$i],$used,$clkm; + $by_times{$i}=$used; + $by_clks{$i}=@clks_noemu[$i]; + } +print "Sorted by consumed clocks:\n\n"; +foreach $key (sort { $by_clks{$b} <=> $by_clks{$a} } keys %by_clks) + { + printf "%5.2f %-20s %8d\n",$by_clks{$key}/$lastClk*100,$names[$key],$by_clks{$key}; + } + + +sub AddSimple +{ + my ($name, $opcode)=@_; + + $names[$id]=$name; + @used_noemu[$id]=@used[$opcode]; + @used_emu[$id]=@used[$opcode+0x100]; + @used_both[$id]=@used[$opcode]+@used[$opcode+0x100]; + @clks_noemu[$id]=@clks[$opcode]; + @clks_emu[$id]=@clks[$opcode+0x100]; + @clks_both[$id]=@clks[$opcode]+@clks[$opcode+0x100]; + $id++; +} + +sub AddEmulate +{ + my ($name, $opcode)=@_; + + $names[$id]=$name; + @used_noemu[$id]=@used[$opcode]; + @used_emu[$id]=@used[$opcode+0x100]; + @used_both[$id]=@used[$opcode]; + @clks_noemu[$id]=@clks[$opcode]; + @clks_emu[$id]=@clks[$opcode+0x100]; + @clks_both[$id]=@clks[$opcode]; + $id++; +} + +sub AddSimpleRange +{ + my ($name, $opStart, $opLast)=@_; + my $i; + + $names[$id]=$name; + for ($i=$opStart; $i<=$opLast; $i++) + { + @used_noemu[$id]+=@used[$i]; + @used_emu[$id]+=@used[$i+0x100]; + @used_both[$id]+=@used[$i]+@used[$i+0x100]; + @clks_noemu[$id]+=@clks[$i]; + @clks_emu[$id]+=@clks[$i+0x100]; + @clks_both[$id]+=@clks[$i]+@clks[$i+0x100]; + } + $id++; +} + + diff --git a/zpu/hdl/zpu4/core/zpu_config.vhd b/zpu/hdl/zpu4/core/zpu_config.vhd new file mode 100644 index 0000000..c678044 --- /dev/null +++ b/zpu/hdl/zpu4/core/zpu_config.vhd @@ -0,0 +1,58 @@ +-- ZPU
+--
+-- Copyright 2004-2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
+--
+-- The FreeBSD license
+--
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions
+-- are met:
+--
+-- 1. Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- 2. Redistributions in binary form must reproduce the above
+-- copyright notice, this list of conditions and the following
+-- disclaimer in the documentation and/or other materials
+-- provided with the distribution.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE ZPU PROJECT ``AS IS'' AND ANY
+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+-- ZPU PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+-- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+-- The views and conclusions contained in the software and documentation
+-- are those of the authors and should not be interpreted as representing
+-- official policies, either expressed or implied, of the ZPU Project.
+
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+package zpu_config is
+
+ -- generate trace output or not.
+ constant Generate_Trace : boolean := false;
+ constant wordPower : integer := 5;
+ -- during simulation, set this to '0' to get matching trace.txt
+ constant DontCareValue : std_logic := 'X';
+ -- Clock frequency in MHz.
+ constant ZPU_Frequency : std_logic_vector(7 downto 0) := x"64";
+ -- This is the msb address bit. bytes=2^(maxAddrBitIncIO+1)
+ constant maxAddrBitIncIO : integer := 15;
+ constant maxAddrBitBRAM : integer := 14;
+
+ -- start byte address of stack.
+ -- point to top of RAM - 2*words
+ constant spStart : std_logic_vector(maxAddrBitIncIO downto 0) :=
+ std_logic_vector(to_unsigned((2**(maxAddrBitBRAM+1))-8, maxAddrBitIncIO+1));
+
+end zpu_config;
diff --git a/zpu/hdl/zpu4/core/zpu_core.vhd b/zpu/hdl/zpu4/core/zpu_core.vhd new file mode 100644 index 0000000..f423f80 --- /dev/null +++ b/zpu/hdl/zpu4/core/zpu_core.vhd @@ -0,0 +1,1014 @@ +-- ZPU +-- +-- Copyright 2004-2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com +-- Copyright 2008 alvieboy - Álvaro Lopes - alvieboy@alvie.com +-- +-- The FreeBSD license +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above +-- copyright notice, this list of conditions and the following +-- disclaimer in the documentation and/or other materials +-- provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY THE ZPU PROJECT ``AS IS'' AND ANY +-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +-- ZPU PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +-- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-- +-- The views and conclusions contained in the software and documentation +-- are those of the authors and should not be interpreted as representing +-- official policies, either expressed or implied, of the ZPU Project. + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; +use work.zpu_config.all; +use work.zpupkg.all; + + +-- mem_writeEnable - set to '1' for a single cycle to send off a write request. +-- mem_write is valid only while mem_writeEnable='1'. +-- mem_readEnable - set to '1' for a single cycle to send off a read request. +-- +-- mem_busy - It is illegal to send off a read/write request when mem_busy='1'. +-- Set to '0' when mem_read is valid after a read request. +-- If it goes to '1'(busy), it is on the cycle after mem_read/writeEnable +-- is '1'. +-- mem_addr - address for read/write request +-- mem_read - read data. Valid only on the cycle after mem_busy='0' after +-- mem_readEnable='1' for a single cycle. +-- mem_write - data to write +-- mem_writeMask - set to '1' for those bits that are to be written to memory upon +-- write request +-- break - set to '1' when CPU hits break instruction +-- interrupt - set to '1' until interrupts are cleared by CPU. + + + + +entity zpu_core is + port ( + clk : in std_logic; + reset : in std_logic; + enable : in std_logic; + in_mem_busy : in std_logic; + mem_read : in std_logic_vector(wordSize-1 downto 0); + mem_write : out std_logic_vector(wordSize-1 downto 0); + out_mem_addr : out std_logic_vector(maxAddrBitIncIO downto 0); + out_mem_writeEnable : out std_logic; + out_mem_readEnable : out std_logic; + mem_writeMask : out std_logic_vector(wordBytes-1 downto 0); + interrupt : in std_logic; + break : out std_logic + ); +end zpu_core; + + +architecture behave of zpu_core is + + type InsnType is ( + Insn_AddTop, + Insn_Dup, + Insn_DupStackB, + Insn_Pop, + Insn_PopDown, + Insn_Add, + Insn_Or, + Insn_And, + Insn_Store, + Insn_AddSP, + Insn_Shift, + Insn_Nop, + Insn_Im, + Insn_LoadSP, + Insn_StoreSP, + Insn_Emulate, + Insn_Load, + Insn_PushSP, + Insn_PopPC, + Insn_PopPCrel, + Insn_Not, + Insn_Flip, + Insn_PopSP, + Insn_Neqbranch, + Insn_Eq, + Insn_Loadb, + Insn_Mult, + Insn_Lessthan, + Insn_Lessthanorequal, + Insn_Ulessthanorequal, + Insn_Ulessthan, + Insn_PushSPadd, + Insn_Call, + Insn_CallPCrel, + Insn_Sub, + Insn_Break, + Insn_Storeb, + Insn_InsnFetch + ); + + type StateType is ( + State_Load2, + State_Popped, + State_LoadSP2, + State_LoadSP3, + State_AddSP2, + State_Fetch, + State_Execute, + State_Decode, + State_Decode2, + State_Resync, + + State_StoreSP2, + State_Resync2, + State_Resync3, + State_Loadb2, + State_Storeb2, + State_Mult2, + State_Mult3, + State_Mult5, + State_Mult4, + State_BinaryOpResult2, + State_BinaryOpResult, + State_Idle, + State_Interrupt + ); + + + signal pc : unsigned(maxAddrBitIncIO downto 0); + signal sp : unsigned(maxAddrBitIncIO downto minAddrBit); + signal incSp : unsigned(maxAddrBitIncIO downto minAddrBit); + signal incIncSp : unsigned(maxAddrBitIncIO downto minAddrBit); + signal decSp : unsigned(maxAddrBitIncIO downto minAddrBit); + signal stackA : unsigned(wordSize-1 downto 0); + signal binaryOpResult : unsigned(wordSize-1 downto 0); + signal binaryOpResult2 : unsigned(wordSize-1 downto 0); + signal multResult2 : unsigned(wordSize-1 downto 0); + signal multResult3 : unsigned(wordSize-1 downto 0); + signal multResult : unsigned(wordSize-1 downto 0); + signal multA : unsigned(wordSize-1 downto 0); + signal multB : unsigned(wordSize-1 downto 0); + signal stackB : unsigned(wordSize-1 downto 0); + signal idim_flag : std_logic; + signal busy : std_logic; + signal mem_writeEnable : std_logic; + signal mem_readEnable : std_logic; + signal mem_addr : std_logic_vector(maxAddrBitIncIO downto minAddrBit); + signal mem_delayAddr : std_logic_vector(maxAddrBitIncIO downto minAddrBit); + signal mem_delayReadEnable : std_logic; + -- + signal inInterrupt : std_logic; + -- + signal decodeWord : std_logic_vector(wordSize-1 downto 0); + -- + -- + signal state : StateType; + signal insn : InsnType; + type InsnArray is array(0 to wordBytes-1) of InsnType; + signal decodedOpcode : InsnArray; + -- + type OpcodeArray is array(0 to wordBytes-1) of std_logic_vector(7 downto 0); + -- + signal opcode : OpcodeArray; + + + + signal begin_inst : std_logic; + signal trace_opcode : std_logic_vector(7 downto 0); + signal trace_pc : std_logic_vector(maxAddrBitIncIO downto 0); + signal trace_sp : std_logic_vector(maxAddrBitIncIO downto minAddrBit); + signal trace_topOfStack : std_logic_vector(wordSize-1 downto 0); + signal trace_topOfStackB : std_logic_vector(wordSize-1 downto 0); + +-- state machine. + +begin + + + traceFileGenerate : + if Generate_Trace generate + trace_file : trace port map ( + clk => clk, + begin_inst => begin_inst, + pc => trace_pc, + opcode => trace_opcode, + sp => trace_sp, + memA => trace_topOfStack, + memB => trace_topOfStackB, + busy => busy, + intsp => (others => 'U') + ); + end generate; + + + -- the memory subsystem will tell us one cycle later whether or + -- not it is busy + out_mem_writeEnable <= mem_writeEnable; + out_mem_readEnable <= mem_readEnable; + out_mem_addr(maxAddrBitIncIO downto minAddrBit) <= mem_addr; + out_mem_addr(minAddrBit-1 downto 0) <= (others => '0'); + + incSp <= sp + 1; + incIncSp <= sp + 2; + decSp <= sp - 1; + + + opcodeControl : process(clk, reset) + variable tOpcode : std_logic_vector(OpCode_Size-1 downto 0); + variable spOffset : unsigned(4 downto 0); + variable tSpOffset : unsigned(4 downto 0); + variable nextPC : unsigned(maxAddrBitIncIO downto 0); + variable tNextInsn : InsnType; + variable tDecodedOpcode : InsnArray; + variable tMultResult : unsigned(wordSize*2-1 downto 0); + begin + if reset = '1' then + state <= State_Idle; + break <= '0'; + sp <= unsigned(spStart(maxAddrBitIncIO downto minAddrBit)); + + pc <= (others => '0'); + idim_flag <= '0'; + begin_inst <= '0'; + inInterrupt <= '0'; + mem_writeEnable <= '0'; + mem_readEnable <= '0'; + multA <= (others => '0'); + multB <= (others => '0'); + mem_writeMask <= (others => '1'); + elsif rising_edge(clk) then + -- we must multiply unconditionally to get pipelined multiplication + tMultResult := multA * multB; + multResult3 <= multResult2; + multResult2 <= multResult; + multResult <= tMultResult(wordSize-1 downto 0); + + + binaryOpResult2 <= binaryOpResult; -- pipeline a bit. + + + multA <= (others => DontCareValue); + multB <= (others => DontCareValue); + + + mem_addr <= (others => DontCareValue); + mem_readEnable <= '0'; + mem_writeEnable <= '0'; + mem_write <= (others => DontCareValue); + + if (mem_writeEnable = '1') and (mem_readEnable = '1') then + report "read/write collision" severity failure; + end if; + + + + + spOffset(4) := not opcode(to_integer(pc(byteBits-1 downto 0)))(4); + spOffset(3 downto 0) := unsigned(opcode(to_integer(pc(byteBits-1 downto 0)))(3 downto 0)); + nextPC := pc + 1; + + -- prepare trace snapshot + trace_opcode <= opcode(to_integer(pc(byteBits-1 downto 0))); + trace_pc <= std_logic_vector(pc); + trace_sp <= std_logic_vector(sp); + trace_topOfStack <= std_logic_vector(stackA); + trace_topOfStackB <= std_logic_vector(stackB); + begin_inst <= '0'; + + if (interrupt = '0') then + -- Interrupt ended, we can serve ISR again + inInterrupt <= '0'; + end if; + + case state is + + when State_Idle => + if enable = '1' then + state <= State_Resync; + end if; + -- Initial state of ZPU, fetch top of stack + first instruction + + when State_Resync => + if in_mem_busy = '0' then + mem_addr <= std_logic_vector(sp); + mem_readEnable <= '1'; + state <= State_Resync2; + end if; + + when State_Resync2 => + if in_mem_busy = '0' then + stackA <= unsigned(mem_read); + mem_addr <= std_logic_vector(incSp); + mem_readEnable <= '1'; + state <= State_Resync3; + end if; + + when State_Resync3 => + if in_mem_busy = '0' then + stackB <= unsigned(mem_read); + mem_addr <= std_logic_vector(pc(maxAddrBitIncIO downto minAddrBit)); + mem_readEnable <= '1'; + state <= State_Decode; + end if; + + when State_Decode => + if in_mem_busy = '0' then + decodeWord <= mem_read; + state <= State_Decode2; + -- Do not recurse into ISR while interrupt line is active + if interrupt = '1' and inInterrupt = '0' and idim_flag = '0' then + -- We got an interrupt, execute interrupt instead of next instruction + inInterrupt <= '1'; + sp <= decSp; + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(incSp); + mem_write <= std_logic_vector(stackB); + stackA <= (others => DontCareValue); + stackA(maxAddrBitIncIO downto 0) <= pc; + stackB <= stackA; + pc <= to_unsigned(32, maxAddrBitIncIO+1); + state <= State_Interrupt; + end if; -- interrupt + end if; -- in_mem_busy + + when State_Interrupt => + if in_mem_busy = '0' then + mem_addr <= std_logic_vector(pc(maxAddrBitIncIO downto minAddrBit)); + mem_readEnable <= '1'; + state <= State_Decode; + report "ZPU jumped to interrupt!" severity note; + end if; + + when State_Decode2 => + -- decode 4 instructions in parallel + for i in 0 to wordBytes-1 loop + tOpcode := decodeWord((wordBytes-1-i+1)*8-1 downto (wordBytes-1-i)*8); + + tSpOffset(4) := not tOpcode(4); + tSpOffset(3 downto 0) := unsigned(tOpcode(3 downto 0)); + + opcode(i) <= tOpcode; + if (tOpcode(7 downto 7) = OpCode_Im) then + tNextInsn := Insn_Im; + elsif (tOpcode(7 downto 5) = OpCode_StoreSP) then + if tSpOffset = 0 then + tNextInsn := Insn_Pop; + elsif tSpOffset = 1 then + tNextInsn := Insn_PopDown; + else + tNextInsn := Insn_StoreSP; + end if; + elsif (tOpcode(7 downto 5) = OpCode_LoadSP) then + if tSpOffset = 0 then + tNextInsn := Insn_Dup; + elsif tSpOffset = 1 then + tNextInsn := Insn_DupStackB; + else + tNextInsn := Insn_LoadSP; + end if; + elsif (tOpcode(7 downto 5) = OpCode_Emulate) then + tNextInsn := Insn_Emulate; + if tOpcode(5 downto 0) = OpCode_Neqbranch then + tNextInsn := Insn_Neqbranch; + elsif tOpcode(5 downto 0) = OpCode_Eq then + tNextInsn := Insn_Eq; + elsif tOpcode(5 downto 0) = OpCode_Lessthan then + tNextInsn := Insn_Lessthan; + elsif tOpcode(5 downto 0) = OpCode_Lessthanorequal then + --tNextInsn :=Insn_Lessthanorequal; + elsif tOpcode(5 downto 0) = OpCode_Ulessthan then + tNextInsn := Insn_Ulessthan; + elsif tOpcode(5 downto 0) = OpCode_Ulessthanorequal then + --tNextInsn :=Insn_Ulessthanorequal; + elsif tOpcode(5 downto 0) = OpCode_Loadb then + tNextInsn := Insn_Loadb; + elsif tOpcode(5 downto 0) = OpCode_Mult then + tNextInsn := Insn_Mult; + elsif tOpcode(5 downto 0) = OpCode_Storeb then + tNextInsn := Insn_Storeb; + elsif tOpcode(5 downto 0) = OpCode_Pushspadd then + tNextInsn := Insn_PushSPadd; + elsif tOpcode(5 downto 0) = OpCode_Callpcrel then + tNextInsn := Insn_CallPCrel; + elsif tOpcode(5 downto 0) = OpCode_Call then + --tNextInsn :=Insn_Call; + elsif tOpcode(5 downto 0) = OpCode_Sub then + tNextInsn := Insn_Sub; + elsif tOpcode(5 downto 0) = OpCode_PopPCRel then + --tNextInsn :=Insn_PopPCrel; + end if; + elsif (tOpcode(7 downto 4) = OpCode_AddSP) then + if tSpOffset = 0 then + tNextInsn := Insn_Shift; + elsif tSpOffset = 1 then + tNextInsn := Insn_AddTop; + else + tNextInsn := Insn_AddSP; + end if; + else + case tOpcode(3 downto 0) is + when OpCode_Nop => + tNextInsn := Insn_Nop; + when OpCode_PushSP => + tNextInsn := Insn_PushSP; + when OpCode_PopPC => + tNextInsn := Insn_PopPC; + when OpCode_Add => + tNextInsn := Insn_Add; + when OpCode_Or => + tNextInsn := Insn_Or; + when OpCode_And => + tNextInsn := Insn_And; + when OpCode_Load => + tNextInsn := Insn_Load; + when OpCode_Not => + tNextInsn := Insn_Not; + when OpCode_Flip => + tNextInsn := Insn_Flip; + when OpCode_Store => + tNextInsn := Insn_Store; + when OpCode_PopSP => + tNextInsn := Insn_PopSP; + when others => + tNextInsn := Insn_Break; + + end case; -- tOpcode(3 downto 0) + end if; -- tOpcode + tDecodedOpcode(i) := tNextInsn; + + end loop; -- 0 to wordBytes-1 + + insn <= tDecodedOpcode(to_integer(pc(byteBits-1 downto 0))); + + -- once we wrap, we need to fetch + tDecodedOpcode(0) := Insn_InsnFetch; + + decodedOpcode <= tDecodedOpcode; + state <= State_Execute; + + + + -- Each instruction must: + -- + -- 1. set idim_flag + -- 2. increase PC if applicable + -- 3. set next state if appliable + -- 4. do it's operation + + when State_Execute => + insn <= decodedOpcode(to_integer(nextPC(byteBits-1 downto 0))); + + case insn is + + when Insn_InsnFetch => + state <= State_Fetch; + + when Insn_Im => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '1'; + pc <= pc + 1; + + if idim_flag = '1' then + stackA(wordSize-1 downto 7) <= stackA(wordSize-8 downto 0); + stackA(6 downto 0) <= unsigned(opcode(to_integer(pc(byteBits-1 downto 0)))(6 downto 0)); + else + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(incSp); + mem_write <= std_logic_vector(stackB); + stackB <= stackA; + sp <= decSp; + for i in wordSize-1 downto 7 loop + stackA(i) <= opcode(to_integer(pc(byteBits-1 downto 0)))(6); + end loop; + stackA(6 downto 0) <= unsigned(opcode(to_integer(pc(byteBits-1 downto 0)))(6 downto 0)); + end if; -- idim_flag + end if; -- in_mem_busy + + when Insn_StoreSP => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + state <= State_StoreSP2; + + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(sp+spOffset); + mem_write <= std_logic_vector(stackA); + stackA <= stackB; + sp <= incSp; + end if; + + + when Insn_LoadSP => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + state <= State_LoadSP2; + + sp <= decSp; + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(incSp); + mem_write <= std_logic_vector(stackB); + end if; + + when Insn_Emulate => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + sp <= decSp; + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(incSp); + mem_write <= std_logic_vector(stackB); + stackA <= (others => DontCareValue); + stackA(maxAddrBitIncIO downto 0) <= pc + 1; + stackB <= stackA; + + -- The emulate address is: + -- 98 7654 3210 + -- 0000 00aa aaa0 0000 + pc <= (others => '0'); + pc(9 downto 5) <= unsigned(opcode(to_integer(pc(byteBits-1 downto 0)))(4 downto 0)); + state <= State_Fetch; + end if; -- in_mem_busy + + when Insn_CallPCrel => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + stackA <= (others => DontCareValue); + stackA(maxAddrBitIncIO downto 0) <= pc + 1; + + pc <= pc + stackA(maxAddrBitIncIO downto 0); + state <= State_Fetch; + end if; + + when Insn_Call => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + stackA <= (others => DontCareValue); + stackA(maxAddrBitIncIO downto 0) <= pc + 1; + pc <= stackA(maxAddrBitIncIO downto 0); + state <= State_Fetch; + end if; + + when Insn_AddSP => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + state <= State_AddSP2; + + mem_readEnable <= '1'; + mem_addr <= std_logic_vector(sp+spOffset); + end if; + + when Insn_PushSP => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + sp <= decSp; + stackA <= (others => '0'); + stackA(maxAddrBitIncIO downto minAddrBit) <= sp; + stackB <= stackA; + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(incSp); + mem_write <= std_logic_vector(stackB); + end if; + + when Insn_PopPC => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + pc <= stackA(maxAddrBitIncIO downto 0); + sp <= incSp; + + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(incSp); + mem_write <= std_logic_vector(stackB); + state <= State_Resync; + end if; + + when Insn_PopPCrel => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + pc <= stackA(maxAddrBitIncIO downto 0) + pc; + sp <= incSp; + + mem_writeEnable <= '1'; + mem_addr <= std_logic_vector(incSp); + mem_write <= std_logic_vector(stackB); + state <= State_Resync; + end if; + + when Insn_Add => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + stackA <= stackA + stackB; + + mem_readEnable <= '1'; + mem_addr <= std_logic_vector(incIncSp); + sp <= incSp; + state <= State_Popped; + end if; + + when Insn_Sub => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + binaryOpResult <= stackB - stackA; + state <= State_BinaryOpResult; + end if; + + when Insn_Pop => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + mem_addr <= std_logic_vector(incIncSp); + mem_readEnable <= '1'; + sp <= incSp; + stackA <= stackB; + state <= State_Popped; + end if; + + when Insn_PopDown => + if in_mem_busy = '0' then + -- PopDown leaves top of stack unchanged + begin_inst <= '1'; + idim_flag <= '0'; + mem_addr <= std_logic_vector(incIncSp); + mem_readEnable <= '1'; + sp <= incSp; + state <= State_Popped; + end if; + + when Insn_Or => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + stackA <= stackA or stackB; + mem_readEnable <= '1'; + mem_addr <= std_logic_vector(incIncSp); + sp <= incSp; + state <= State_Popped; + end if; + + when Insn_And => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + + stackA <= stackA and stackB; + mem_readEnable <= '1'; + mem_addr <= std_logic_vector(incIncSp); + sp <= incSp; + state <= State_Popped; + end if; + + when Insn_Eq => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + + binaryOpResult <= (others => '0'); + if (stackA = stackB) then + binaryOpResult(0) <= '1'; + end if; + state <= State_BinaryOpResult; + end if; + + when Insn_Ulessthan => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + + binaryOpResult <= (others => '0'); + if (stackA < stackB) then + binaryOpResult(0) <= '1'; + end if; + state <= State_BinaryOpResult; + end if; + + when Insn_Ulessthanorequal => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + + binaryOpResult <= (others => '0'); + if (stackA <= stackB) then + binaryOpResult(0) <= '1'; + end if; + state <= State_BinaryOpResult; + end if; + + when Insn_Lessthan => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + + binaryOpResult <= (others => '0'); + if (signed(stackA) < signed(stackB)) then + binaryOpResult(0) <= '1'; + end if; + state <= State_BinaryOpResult; + end if; + + when Insn_Lessthanorequal => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + + binaryOpResult <= (others => '0'); + if (signed(stackA) <= signed(stackB)) then + binaryOpResult(0) <= '1'; + end if; + state <= State_BinaryOpResult; + end if; + + when Insn_Load => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + state <= State_Load2; + + mem_addr <= std_logic_vector(stackA(maxAddrBitIncIO downto minAddrBit)); + mem_readEnable <= '1'; + end if; + + when Insn_Dup => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + sp <= decSp; + stackB <= stackA; + mem_write <= std_logic_vector(stackB); + mem_addr <= std_logic_vector(incSp); + mem_writeEnable <= '1'; + end if; + + when Insn_DupStackB => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + sp <= decSp; + stackA <= stackB; + stackB <= stackA; + mem_write <= std_logic_vector(stackB); + mem_addr <= std_logic_vector(incSp); + mem_writeEnable <= '1'; + end if; + + when Insn_Store => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + mem_addr <= std_logic_vector(stackA(maxAddrBitIncIO downto minAddrBit)); + mem_write <= std_logic_vector(stackB); + mem_writeEnable <= '1'; + sp <= incIncSp; + state <= State_Resync; + end if; + + when Insn_PopSP => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + mem_write <= std_logic_vector(stackB); + mem_addr <= std_logic_vector(incSp); + mem_writeEnable <= '1'; + sp <= stackA(maxAddrBitIncIO downto minAddrBit); + state <= State_Resync; + end if; + + when Insn_Nop => + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + when Insn_Not => + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + stackA <= not stackA; + + when Insn_Flip => + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + for i in 0 to wordSize-1 loop + stackA(i) <= stackA(wordSize-1-i); + end loop; + + when Insn_AddTop => + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + stackA <= stackA + stackB; + + when Insn_Shift => + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + stackA(wordSize-1 downto 1) <= stackA(wordSize-2 downto 0); + stackA(0) <= '0'; + + when Insn_PushSPadd => + begin_inst <= '1'; + idim_flag <= '0'; + pc <= pc + 1; + + stackA <= (others => '0'); + stackA(maxAddrBitIncIO downto minAddrBit) <= stackA(maxAddrBitIncIO-minAddrBit downto 0)+sp; + + when Insn_Neqbranch => + -- branches are almost always taken as they form loops + begin_inst <= '1'; + idim_flag <= '0'; + sp <= incIncSp; + if (stackB /= 0) then + pc <= stackA(maxAddrBitIncIO downto 0) + pc; + else + pc <= pc + 1; + end if; + -- need to fetch stack again. + state <= State_Resync; + + when Insn_Mult => + begin_inst <= '1'; + idim_flag <= '0'; + + multA <= stackA; + multB <= stackB; + state <= State_Mult2; + + when Insn_Break => + report "Break instruction encountered" severity failure; + break <= '1'; + + when Insn_Loadb => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + state <= State_Loadb2; + + mem_addr <= std_logic_vector(stackA(maxAddrBitIncIO downto minAddrBit)); + mem_readEnable <= '1'; + end if; + + when Insn_Storeb => + if in_mem_busy = '0' then + begin_inst <= '1'; + idim_flag <= '0'; + state <= State_Storeb2; + + mem_addr <= std_logic_vector(stackA(maxAddrBitIncIO downto minAddrBit)); + mem_readEnable <= '1'; + end if; + + when others => + sp <= (others => DontCareValue); + report "Illegal instruction" severity failure; + break <= '1'; + + end case; -- insn/State_Execute + + + when State_StoreSP2 => + if in_mem_busy = '0' then + mem_addr <= std_logic_vector(incSp); + mem_readEnable <= '1'; + state <= State_Popped; + end if; + + when State_LoadSP2 => + if in_mem_busy = '0' then + state <= State_LoadSP3; + mem_readEnable <= '1'; + mem_addr <= std_logic_vector(sp+spOffset+1); + end if; + + when State_LoadSP3 => + if in_mem_busy = '0' then + pc <= pc + 1; + state <= State_Execute; + stackB <= stackA; + stackA <= unsigned(mem_read); + end if; + + when State_AddSP2 => + if in_mem_busy = '0' then + pc <= pc + 1; + state <= State_Execute; + stackA <= stackA + unsigned(mem_read); + end if; + + when State_Load2 => + if in_mem_busy = '0' then + stackA <= unsigned(mem_read); + pc <= pc + 1; + state <= State_Execute; + end if; + + when State_Loadb2 => + if in_mem_busy = '0' then + stackA <= (others => '0'); + stackA(7 downto 0) <= unsigned(mem_read(((wordBytes-1-to_integer(stackA(byteBits-1 downto 0)))*8+7) downto (wordBytes-1-to_integer(stackA(byteBits-1 downto 0)))*8)); + pc <= pc + 1; + state <= State_Execute; + end if; + + when State_Storeb2 => + if in_mem_busy = '0' then + mem_addr <= std_logic_vector(stackA(maxAddrBitIncIO downto minAddrBit)); + mem_write <= mem_read; + mem_write(((wordBytes-1-to_integer(stackA(byteBits-1 downto 0)))*8+7) downto (wordBytes-1-to_integer(stackA(byteBits-1 downto 0)))*8) <= std_logic_vector(stackB(7 downto 0)); + mem_writeEnable <= '1'; + pc <= pc + 1; + sp <= incIncSp; + state <= State_Resync; + end if; + + when State_Fetch => + if in_mem_busy = '0' then + mem_addr <= std_logic_vector(pc(maxAddrBitIncIO downto minAddrBit)); + mem_readEnable <= '1'; + state <= State_Decode; + end if; + + when State_Mult2 => + state <= State_Mult3; + + when State_Mult3 => + state <= State_Mult4; + + when State_Mult4 => + state <= State_Mult5; + + when State_Mult5 => + if in_mem_busy = '0' then + stackA <= multResult3; + mem_readEnable <= '1'; + mem_addr <= std_logic_vector(incIncSp); + sp <= incSp; + state <= State_Popped; + end if; + + when State_BinaryOpResult => + state <= State_BinaryOpResult2; + + when State_BinaryOpResult2 => + mem_readEnable <= '1'; + mem_addr <= std_logic_vector(incIncSp); + sp <= incSp; + stackA <= binaryOpResult2; + state <= State_Popped; + + when State_Popped => + if in_mem_busy = '0' then + pc <= pc + 1; + stackB <= unsigned(mem_read); + state <= State_Execute; + end if; + + when others => + sp <= (others => DontCareValue); + report "Illegal state" severity failure; + break <= '1'; + + end case; -- state + end if; -- clk'event + end process; + + + +end behave; diff --git a/zpu/hdl/zpu4/core/zpu_core_small.vhd b/zpu/hdl/zpu4/core/zpu_core_small.vhd new file mode 100644 index 0000000..9ac35a8 --- /dev/null +++ b/zpu/hdl/zpu4/core/zpu_core_small.vhd @@ -0,0 +1,602 @@ +-- ZPU +-- +-- Copyright 2004-2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com +-- +-- The FreeBSD license +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above +-- copyright notice, this list of conditions and the following +-- disclaimer in the documentation and/or other materials +-- provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY THE ZPU PROJECT ``AS IS'' AND ANY +-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +-- ZPU PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +-- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-- +-- The views and conclusions contained in the software and documentation +-- are those of the authors and should not be interpreted as representing +-- official policies, either expressed or implied, of the ZPU Project. + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; +use work.zpu_config.all; +use work.zpupkg.all; + + +entity zpu_core is + port ( + clk : in std_logic; + -- asynchronous reset signal + reset : in std_logic; + -- this particular implementation of the ZPU does not + -- have a clocked enable signal + enable : in std_logic; + in_mem_busy : in std_logic; + mem_read : in std_logic_vector(wordSize-1 downto 0); + mem_write : out std_logic_vector(wordSize-1 downto 0); + out_mem_addr : out std_logic_vector(maxAddrBitIncIO downto 0); + out_mem_writeEnable : out std_logic; + out_mem_readEnable : out std_logic; + -- this implementation of the ZPU *always* reads and writes entire + -- 32 bit words, so mem_writeMask is tied to (others => '1'). + mem_writeMask : out std_logic_vector(wordBytes-1 downto 0); + -- Set to one to jump to interrupt vector + -- The ZPU will communicate with the hardware that caused the + -- interrupt via memory mapped IO or the interrupt flag can + -- be cleared automatically + interrupt : in std_logic; + -- Signal that the break instruction is executed, normally only used + -- in simulation to stop simulation + break : out std_logic + ); +end zpu_core; + + + +architecture behave of zpu_core is + + signal memAWriteEnable : std_logic; + signal memAAddr : unsigned(maxAddrBit downto minAddrBit); + signal memAWrite : unsigned(wordSize-1 downto 0); + signal memARead : unsigned(wordSize-1 downto 0); + signal memBWriteEnable : std_logic; + signal memBAddr : unsigned(maxAddrBit downto minAddrBit); + signal memBWrite : unsigned(wordSize-1 downto 0); + signal memBRead : unsigned(wordSize-1 downto 0); + + + + signal pc : unsigned(maxAddrBit downto 0); + signal sp : unsigned(maxAddrBit downto minAddrBit); + + -- this signal is set upon executing an IM instruction + -- the subsequence IM instruction will then behave differently. + -- all other instructions will clear the idim_flag. + -- this yields highly compact immediate instructions. + signal idim_flag : std_logic; + -- + signal busy : std_logic; + -- + signal begin_inst : std_logic; + + + signal trace_opcode : std_logic_vector(7 downto 0); + signal trace_pc : std_logic_vector(maxAddrBitIncIO downto 0); + signal trace_sp : std_logic_vector(maxAddrBitIncIO downto minAddrBit); + signal trace_topOfStack : std_logic_vector(wordSize-1 downto 0); + signal trace_topOfStackB : std_logic_vector(wordSize-1 downto 0); + + -- state machine. + type State_Type is ( + State_Fetch, + State_WriteIODone, + State_Execute, + State_StoreToStack, + State_Add, + State_Or, + State_And, + State_Store, + State_ReadIO, + State_WriteIO, + State_Load, + State_FetchNext, + State_AddSP, + State_ReadIODone, + State_Decode, + State_Resync, + State_Interrupt + ); + + type DecodedOpcodeType is ( + Decoded_Nop, + Decoded_Im, + Decoded_ImShift, + Decoded_LoadSP, + Decoded_StoreSP , + Decoded_AddSP, + Decoded_Emulate, + Decoded_Break, + Decoded_PushSP, + Decoded_PopPC, + Decoded_Add, + Decoded_Or, + Decoded_And, + Decoded_Load, + Decoded_Not, + Decoded_Flip, + Decoded_Store, + Decoded_PopSP, + Decoded_Interrupt + ); + + + + signal sampledOpcode : std_logic_vector(OpCode_Size-1 downto 0); + signal opcode : std_logic_vector(OpCode_Size-1 downto 0); + -- + signal decodedOpcode : DecodedOpcodeType; + signal sampledDecodedOpcode : DecodedOpcodeType; + + + signal state : State_Type; + -- + subtype AddrBitBRAM_range is natural range maxAddrBitBRAM downto minAddrBit; + signal memAAddr_stdlogic : std_logic_vector(AddrBitBRAM_range); + signal memAWrite_stdlogic : std_logic_vector(memAWrite'range); + signal memARead_stdlogic : std_logic_vector(memARead'range); + signal memBAddr_stdlogic : std_logic_vector(AddrBitBRAM_range); + signal memBWrite_stdlogic : std_logic_vector(memBWrite'range); + signal memBRead_stdlogic : std_logic_vector(memBRead'range); + -- + subtype index is integer range 0 to 3; + -- + signal tOpcode_sel : index; + -- + signal inInterrupt : std_logic; + + + +begin + + -- generate a trace file. + -- + -- This is only used in simulation to see what instructions are + -- executed. + -- + -- a quick & dirty regression test is then to commit trace files + -- to CVS and compare the latest trace file against the last known + -- good trace file + traceFileGenerate : if Generate_Trace generate + trace_file : trace port map ( + clk => clk, + begin_inst => begin_inst, + pc => trace_pc, + opcode => trace_opcode, + sp => trace_sp, + memA => trace_topOfStack, + memB => trace_topOfStackB, + busy => busy, + intsp => (others => 'U') + ); + end generate; + + + -- mem_writeMask is not used in this design, tie it to 1 + mem_writeMask <= (others => '1'); + + + + memAAddr_stdlogic <= std_logic_vector(memAAddr(AddrBitBRAM_range)); + memAWrite_stdlogic <= std_logic_vector(memAWrite); + memBAddr_stdlogic <= std_logic_vector(memBAddr(AddrBitBRAM_range)); + memBWrite_stdlogic <= std_logic_vector(memBWrite); + + + -- dualport_ram must be defined by the application. + -- + -- How this can be implemented is highly dependent on the FPGA + -- and synthesis technology used. + -- + -- sometimes it can be instantiated as in the + -- zpu/example/helloworld.vhd, using inference, + -- but oftentimes it must be instantiated directly + -- portmapping to part specific FPGA resources + -- + -- + -- DANGER!!!!!! If inference fails, then synthesis will try + -- to implement the memory using basic logic resources. This + -- will almost certainly cause the compiler to get "stuck" + -- since synthesising such a huge number of basic logic resources + -- will take more or less forever. + -- + -- So: if your compiler gets "stuck" then inference is not + -- the way to go. + memory : dualport_ram port map ( + clk => clk, + memAWriteEnable => memAWriteEnable, + memAAddr => memAAddr_stdlogic, + memAWrite => memAWrite_stdlogic, + memARead => memARead_stdlogic, + memBWriteEnable => memBWriteEnable, + memBAddr => memBAddr_stdlogic, + memBWrite => memBWrite_stdlogic, + memBRead => memBRead_stdlogic + ); + memARead <= unsigned(memARead_stdlogic); + memBRead <= unsigned(memBRead_stdlogic); + + + + tOpcode_sel <= to_integer(pc(minAddrBit-1 downto 0)); + + + + -- move out calculation of the opcode to a seperate process + -- to make things a bit easier to read + decodeControl : process(memBRead, pc, tOpcode_sel) + variable tOpcode : std_logic_vector(OpCode_Size-1 downto 0); + begin + + -- simplify opcode selection a bit so it passes more synthesizers + case (tOpcode_sel) is + + when 0 => tOpcode := std_logic_vector(memBRead(31 downto 24)); + + when 1 => tOpcode := std_logic_vector(memBRead(23 downto 16)); + + when 2 => tOpcode := std_logic_vector(memBRead(15 downto 8)); + + when 3 => tOpcode := std_logic_vector(memBRead(7 downto 0)); + + when others => tOpcode := std_logic_vector(memBRead(7 downto 0)); + end case; + + sampledOpcode <= tOpcode; + + if (tOpcode(7 downto 7) = OpCode_Im) then + sampledDecodedOpcode <= Decoded_Im; + elsif (tOpcode(7 downto 5) = OpCode_StoreSP) then + sampledDecodedOpcode <= Decoded_StoreSP; + elsif (tOpcode(7 downto 5) = OpCode_LoadSP) then + sampledDecodedOpcode <= Decoded_LoadSP; + elsif (tOpcode(7 downto 5) = OpCode_Emulate) then + sampledDecodedOpcode <= Decoded_Emulate; + elsif (tOpcode(7 downto 4) = OpCode_AddSP) then + sampledDecodedOpcode <= Decoded_AddSP; + else + case tOpcode(3 downto 0) is + when OpCode_Break => + sampledDecodedOpcode <= Decoded_Break; + when OpCode_PushSP => + sampledDecodedOpcode <= Decoded_PushSP; + when OpCode_PopPC => + sampledDecodedOpcode <= Decoded_PopPC; + when OpCode_Add => + sampledDecodedOpcode <= Decoded_Add; + when OpCode_Or => + sampledDecodedOpcode <= Decoded_Or; + when OpCode_And => + sampledDecodedOpcode <= Decoded_And; + when OpCode_Load => + sampledDecodedOpcode <= Decoded_Load; + when OpCode_Not => + sampledDecodedOpcode <= Decoded_Not; + when OpCode_Flip => + sampledDecodedOpcode <= Decoded_Flip; + when OpCode_Store => + sampledDecodedOpcode <= Decoded_Store; + when OpCode_PopSP => + sampledDecodedOpcode <= Decoded_PopSP; + when others => + sampledDecodedOpcode <= Decoded_Nop; + end case; -- tOpcode(3 downto 0) + end if; -- tOpcode + end process; + + + opcodeControl: process(clk, reset) + variable spOffset : unsigned(4 downto 0); + begin + + if reset = '1' then + state <= State_Resync; + break <= '0'; + sp <= unsigned(spStart(maxAddrBit downto minAddrBit)); + pc <= (others => '0'); + idim_flag <= '0'; + begin_inst <= '0'; + memAAddr <= (others => '0'); + memBAddr <= (others => '0'); + memAWriteEnable <= '0'; + memBWriteEnable <= '0'; + out_mem_writeEnable <= '0'; + out_mem_readEnable <= '0'; + memAWrite <= (others => '0'); + memBWrite <= (others => '0'); + inInterrupt <= '0'; + + elsif (clk'event and clk = '1') then + memAWriteEnable <= '0'; + memBWriteEnable <= '0'; + -- This saves ca. 100 LUT's, by explicitly declaring that the + -- memAWrite can be left at whatever value if memAWriteEnable is + -- not set. + memAWrite <= (others => DontCareValue); + memBWrite <= (others => DontCareValue); +-- out_mem_addr <= (others => DontCareValue); +-- mem_write <= (others => DontCareValue); + spOffset := (others => DontCareValue); + memAAddr <= (others => DontCareValue); + memBAddr <= (others => DontCareValue); + + out_mem_writeEnable <= '0'; + out_mem_readEnable <= '0'; + begin_inst <= '0'; + out_mem_addr <= std_logic_vector(memARead(maxAddrBitIncIO downto 0)); + mem_write <= std_logic_vector(memBRead); + + decodedOpcode <= sampledDecodedOpcode; + opcode <= sampledOpcode; + if interrupt = '0' then + inInterrupt <= '0'; -- no longer in an interrupt + end if; + + case state is + + when State_Execute => + state <= State_Fetch; + -- at this point: + -- memBRead contains opcode word + -- memARead contains top of stack + pc <= pc + 1; + + -- trace + begin_inst <= '1'; + trace_pc <= (others => '0'); + trace_pc(maxAddrBit downto 0) <= std_logic_vector(pc); + trace_opcode <= opcode; + trace_sp <= (others => '0'); + trace_sp(maxAddrBit downto minAddrBit) <= std_logic_vector(sp); + trace_topOfStack <= std_logic_vector(memARead); + trace_topOfStackB <= std_logic_vector(memBRead); + + -- during the next cycle we'll be reading the next opcode + spOffset(4) := not opcode(4); + spOffset(3 downto 0) := unsigned(opcode(3 downto 0)); + + idim_flag <= '0'; + + case decodedOpcode is + + when Decoded_Interrupt => + sp <= sp - 1; + memAAddr <= sp - 1; + memAWriteEnable <= '1'; + memAWrite <= (others => DontCareValue); + memAWrite(maxAddrBit downto 0) <= pc; + pc <= to_unsigned(32, maxAddrBit+1); -- interrupt address + report "ZPU jumped to interrupt!" severity note; + + when Decoded_Im => + idim_flag <= '1'; + memAWriteEnable <= '1'; + if (idim_flag = '0') then + sp <= sp - 1; + memAAddr <= sp-1; + for i in wordSize-1 downto 7 loop + memAWrite(i) <= opcode(6); + end loop; + memAWrite(6 downto 0) <= unsigned(opcode(6 downto 0)); + else + memAAddr <= sp; + memAWrite(wordSize-1 downto 7) <= memARead(wordSize-8 downto 0); + memAWrite(6 downto 0) <= unsigned(opcode(6 downto 0)); + end if; -- idim_flag + + when Decoded_StoreSP => + memBWriteEnable <= '1'; + memBAddr <= sp+spOffset; + memBWrite <= memARead; + sp <= sp + 1; + state <= State_Resync; + + when Decoded_LoadSP => + sp <= sp - 1; + memAAddr <= sp+spOffset; + + when Decoded_Emulate => + sp <= sp - 1; + memAWriteEnable <= '1'; + memAAddr <= sp - 1; + memAWrite <= (others => DontCareValue); + memAWrite(maxAddrBit downto 0) <= pc + 1; + -- The emulate address is: + -- 98 7654 3210 + -- 0000 00aa aaa0 0000 + pc <= (others => '0'); + pc(9 downto 5) <= unsigned(opcode(4 downto 0)); + + when Decoded_AddSP => + memAAddr <= sp; + memBAddr <= sp+spOffset; + state <= State_AddSP; + + when Decoded_Break => + report "Break instruction encountered" severity failure; + break <= '1'; + + when Decoded_PushSP => + memAWriteEnable <= '1'; + memAAddr <= sp - 1; + sp <= sp - 1; + memAWrite <= (others => DontCareValue); + memAWrite(maxAddrBit downto minAddrBit) <= sp; + + when Decoded_PopPC => + pc <= memARead(maxAddrBit downto 0); + sp <= sp + 1; + state <= State_Resync; + + when Decoded_Add => + sp <= sp + 1; + state <= State_Add; + + when Decoded_Or => + sp <= sp + 1; + state <= State_Or; + + when Decoded_And => + sp <= sp + 1; + state <= State_And; + + when Decoded_Load => + if (memARead(ioBit) = '1') then + out_mem_addr <= std_logic_vector(memARead(maxAddrBitIncIO downto 0)); + out_mem_readEnable <= '1'; + state <= State_ReadIO; + else + memAAddr <= memARead(maxAddrBit downto minAddrBit); + end if; + + when Decoded_Not => + memAAddr <= sp(maxAddrBit downto minAddrBit); + memAWriteEnable <= '1'; + memAWrite <= not memARead; + + when Decoded_Flip => + memAAddr <= sp(maxAddrBit downto minAddrBit); + memAWriteEnable <= '1'; + for i in 0 to wordSize-1 loop + memAWrite(i) <= memARead(wordSize-1-i); + end loop; + + when Decoded_Store => + memBAddr <= sp + 1; + sp <= sp + 1; + if (memARead(ioBit) = '1') then + state <= State_WriteIO; + else + state <= State_Store; + end if; + + when Decoded_PopSP => + sp <= memARead(maxAddrBit downto minAddrBit); + state <= State_Resync; + + when Decoded_Nop => + memAAddr <= sp; + + when others => + null; + + end case; -- decodedOpcode + + when State_ReadIO => + memAAddr <= sp; + if (in_mem_busy = '0') then + state <= State_Fetch; + memAWriteEnable <= '1'; + memAWrite <= unsigned(mem_read); + end if; + + when State_WriteIO => + sp <= sp + 1; + out_mem_writeEnable <= '1'; + out_mem_addr <= std_logic_vector(memARead(maxAddrBitIncIO downto 0)); + mem_write <= std_logic_vector(memBRead); + state <= State_WriteIODone; + + when State_WriteIODone => + if (in_mem_busy = '0') then + state <= State_Resync; + end if; + + when State_Fetch => + -- We need to resync. During the *next* cycle + -- we'll fetch the opcode @ pc and thus it will + -- be available for State_Execute the cycle after + -- next + memBAddr <= pc(maxAddrBit downto minAddrBit); + state <= State_FetchNext; + + when State_FetchNext => + -- at this point memARead contains the value that is either + -- from the top of stack or should be copied to the top of the stack + memAWriteEnable <= '1'; + memAWrite <= memARead; + memAAddr <= sp; + memBAddr <= sp + 1; + state <= State_Decode; + + when State_Decode => + if interrupt = '1' and inInterrupt = '0' and idim_flag = '0' then + -- We got an interrupt, execute interrupt instead of next instruction + inInterrupt <= '1'; + decodedOpcode <= Decoded_Interrupt; + end if; + -- during the State_Execute cycle we'll be fetching SP+1 + memAAddr <= sp; + memBAddr <= sp + 1; + state <= State_Execute; + + when State_Store => + sp <= sp + 1; + memAWriteEnable <= '1'; + memAAddr <= memARead(maxAddrBit downto minAddrBit); + memAWrite <= memBRead; + state <= State_Resync; + + when State_AddSP => + state <= State_Add; + + when State_Add => + memAAddr <= sp; + memAWriteEnable <= '1'; + memAWrite <= memARead + memBRead; + state <= State_Fetch; + + when State_Or => + memAAddr <= sp; + memAWriteEnable <= '1'; + memAWrite <= memARead or memBRead; + state <= State_Fetch; + + when State_Resync => + memAAddr <= sp; + state <= State_Fetch; + + when State_And => + memAAddr <= sp; + memAWriteEnable <= '1'; + memAWrite <= memARead and memBRead; + state <= State_Fetch; + + when others => + null; + + end case; -- state + + end if; -- reset, enable + end process; + + + +end behave; diff --git a/zpu/hdl/zpu4/core/zpupkg.vhd b/zpu/hdl/zpu4/core/zpupkg.vhd new file mode 100644 index 0000000..0363aca --- /dev/null +++ b/zpu/hdl/zpu4/core/zpupkg.vhd @@ -0,0 +1,218 @@ +-- ZPU +-- +-- Copyright 2004-2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com +-- +-- The FreeBSD license +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above +-- copyright notice, this list of conditions and the following +-- disclaimer in the documentation and/or other materials +-- provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY THE ZPU PROJECT ``AS IS'' AND ANY +-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +-- ZPU PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +-- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-- +-- The views and conclusions contained in the software and documentation +-- are those of the authors and should not be interpreted as representing +-- official policies, either expressed or implied, of the ZPU Project. + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; +use work.zpu_config.all; + + +package zpupkg is + + -- This bit is set for read/writes to IO + -- FIX!!! eventually this should be set to wordSize-1 so as to + -- to make the address of IO independent of amount of memory + -- reserved for CPU. Requires trivial tweaks in toolchain/runtime + -- libraries. + + constant byteBits : integer := wordPower-3; -- # of bits in a word that addresses bytes + constant maxAddrBit : integer := maxAddrBitIncIO-1; + constant ioBit : integer := maxAddrBit+1; + constant wordSize : integer := 2**wordPower; + constant wordBytes : integer := wordSize/8; + constant minAddrBit : integer := byteBits; + -- configurable internal stack size. Probably going to be 16 after toolchain is done + constant stack_bits : integer := 5; + constant stack_size : integer := 2**stack_bits; + + + ------------------------------------------------------------ + -- components + + component dualport_ram is + port ( + clk : in std_logic; + memAWriteEnable : in std_logic; + memAAddr : in std_logic_vector(maxAddrBitBRAM downto minAddrBit); + memAWrite : in std_logic_vector(wordSize-1 downto 0); + memARead : out std_logic_vector(wordSize-1 downto 0); + memBWriteEnable : in std_logic; + memBAddr : in std_logic_vector(maxAddrBitBRAM downto minAddrBit); + memBWrite : in std_logic_vector(wordSize-1 downto 0); + memBRead : out std_logic_vector(wordSize-1 downto 0) + ); + end component dualport_ram; + + + component dram is + port ( + clk : in std_logic; + areset : in std_logic; + mem_writeEnable : in std_logic; + mem_readEnable : in std_logic; + mem_addr : in std_logic_vector(maxAddrBit downto 0); + mem_write : in std_logic_vector(wordSize-1 downto 0); + mem_read : out std_logic_vector(wordSize-1 downto 0); + mem_busy : out std_logic; + mem_writeMask : in std_logic_vector(wordBytes-1 downto 0) + ); + end component dram; + + + component trace is + port ( + clk : in std_logic; + begin_inst : in std_logic; + pc : in std_logic_vector(maxAddrBitIncIO downto 0); + opcode : in std_logic_vector(7 downto 0); + sp : in std_logic_vector(maxAddrBitIncIO downto minAddrBit); + memA : in std_logic_vector(wordSize-1 downto 0); + memB : in std_logic_vector(wordSize-1 downto 0); + busy : in std_logic; + intSp : in std_logic_vector(stack_bits-1 downto 0) + ); + end component trace; + + + component zpu_core is + port ( + clk : in std_logic; + reset : in std_logic; + enable : in std_logic; + in_mem_busy : in std_logic; + mem_read : in std_logic_vector(wordSize-1 downto 0); + mem_write : out std_logic_vector(wordSize-1 downto 0); + out_mem_addr : out std_logic_vector(maxAddrBitIncIO downto 0); + out_mem_writeEnable : out std_logic; + out_mem_readEnable : out std_logic; + mem_writeMask : out std_logic_vector(wordBytes-1 downto 0); + interrupt : in std_logic; + break : out std_logic + ); + end component zpu_core; + + + component timer is + port ( + clk : in std_logic; + areset : in std_logic; + we : in std_logic; + din : in std_logic_vector(7 downto 0); + adr : in std_logic_vector(2 downto 0); + dout : out std_logic_vector(7 downto 0) + ); + end component timer; + + + component zpuio is + port ( + areset : in std_logic; + cpu_clk : in std_logic; + clk_status : in std_logic_vector(2 downto 0); + cpu_din : in std_logic_vector(15 downto 0); + cpu_a : in std_logic_vector(20 downto 0); + cpu_we : in std_logic_vector(1 downto 0); + cpu_re : in std_logic; + cpu_dout : inout std_logic_vector(15 downto 0) + ); + end component zpuio; + + + ------------------------------------------------------------ + -- constants + + -- opcode decode constants + constant OpCode_Im : std_logic_vector(7 downto 7) := "1"; + constant OpCode_StoreSP : std_logic_vector(7 downto 5) := "010"; + constant OpCode_LoadSP : std_logic_vector(7 downto 5) := "011"; + constant OpCode_Emulate : std_logic_vector(7 downto 5) := "001"; + constant OpCode_AddSP : std_logic_vector(7 downto 4) := "0001"; + constant OpCode_Short : std_logic_vector(7 downto 4) := "0000"; + -- + constant OpCode_Break : std_logic_vector(3 downto 0) := "0000"; + constant OpCode_NA4 : std_logic_vector(3 downto 0) := "0001"; + constant OpCode_PushSP : std_logic_vector(3 downto 0) := "0010"; + constant OpCode_NA3 : std_logic_vector(3 downto 0) := "0011"; + -- + constant OpCode_PopPC : std_logic_vector(3 downto 0) := "0100"; + constant OpCode_Add : std_logic_vector(3 downto 0) := "0101"; + constant OpCode_And : std_logic_vector(3 downto 0) := "0110"; + constant OpCode_Or : std_logic_vector(3 downto 0) := "0111"; + -- + constant OpCode_Load : std_logic_vector(3 downto 0) := "1000"; + constant OpCode_Not : std_logic_vector(3 downto 0) := "1001"; + constant OpCode_Flip : std_logic_vector(3 downto 0) := "1010"; + constant OpCode_Nop : std_logic_vector(3 downto 0) := "1011"; + -- + constant OpCode_Store : std_logic_vector(3 downto 0) := "1100"; + constant OpCode_PopSP : std_logic_vector(3 downto 0) := "1101"; + constant OpCode_NA2 : std_logic_vector(3 downto 0) := "1110"; + constant OpCode_NA : std_logic_vector(3 downto 0) := "1111"; + -- + constant OpCode_Lessthan : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(36, 6)); + constant OpCode_Lessthanorequal : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(37, 6)); + constant OpCode_Ulessthan : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(38, 6)); + constant OpCode_Ulessthanorequal : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(39, 6)); + -- + constant OpCode_Swap : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(40, 6)); + constant OpCode_Mult : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(41, 6)); + -- + constant OpCode_Lshiftright : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(42, 6)); + constant OpCode_Ashiftleft : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(43, 6)); + constant OpCode_Ashiftright : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(44, 6)); + constant OpCode_Call : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(45, 6)); + -- + constant OpCode_Eq : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(46, 6)); + constant OpCode_Neq : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(47, 6)); + -- + constant OpCode_Sub : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(49, 6)); + constant OpCode_Loadb : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(51, 6)); + constant OpCode_Storeb : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(52, 6)); + -- + constant OpCode_Eqbranch : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(55, 6)); + constant OpCode_Neqbranch : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(56, 6)); + constant OpCode_Poppcrel : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(57, 6)); + -- + constant OpCode_Pushspadd : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(61, 6)); + constant OpCode_Mult16x16 : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(62, 6)); + constant OpCode_Callpcrel : std_logic_vector(5 downto 0) := std_logic_vector(to_unsigned(63, 6)); + -- + -- + constant OpCode_Size : integer := 8; + + + +end zpupkg; |