diff --git a/rtl/w11a/pdp11_dmpcnt.vbom b/rtl/w11a/pdp11_dmpcnt.vbom new file mode 100644 index 00000000..228dd4a7 --- /dev/null +++ b/rtl/w11a/pdp11_dmpcnt.vbom @@ -0,0 +1,10 @@ +# libs +../vlib/slvtypes.vhd +../vlib/memlib/memlib.vhd +../vlib/rbus/rblib.vhd +# components +[sim]../vlib/memlib/ram_1swar_gen.vbom +[xst]../vlib/memlib/ram_1swar_gen_unisim.vbom +[vsyn]../vlib/memlib/ram_1swar_gen.vbom +# design +pdp11_dmpcnt.vhd diff --git a/rtl/w11a/pdp11_dmpcnt.vhd b/rtl/w11a/pdp11_dmpcnt.vhd new file mode 100644 index 00000000..8d5fb181 --- /dev/null +++ b/rtl/w11a/pdp11_dmpcnt.vhd @@ -0,0 +1,382 @@ +-- $Id: pdp11_dmpcnt.vhd 1051 2018-09-29 15:29:11Z mueller $ +-- +-- Copyright 2018- by Walter F.J. Mueller +-- +-- This program is free software; you may redistribute and/or modify it under +-- the terms of the GNU General Public License as published by the Free +-- Software Foundation, either version 3, or (at your option) any later version. +-- +-- 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 complete details. +-- +------------------------------------------------------------------------------ +-- Module Name: pdp11_dmpcnt - syn +-- Description: pdp11: debug&moni: performance counters +-- +-- Dependencies: - +-- Test bench: - +-- +-- Target Devices: generic +-- Tool versions: ise 14.7; viv 2017.2-2018.2; ghdl 0.34 +-- +-- Synthesized (xst): +-- Date Rev ise Target flop lutl lutm slic t peri +-- 2018-09-23 1050 14.7 131013 xc6slx16-2 250 337 20 121 s 6.5 +-- +-- Revision History: - +-- Date Rev Version Comment +-- 2018-09-29 1051 1.0 Initial version +-- 2018-09-23 1050 0.1 First draft +------------------------------------------------------------------------------ +-- +-- rbus registers: +-- +-- Addr Bits Name r/w/f Function +-- 00 cntl -/w/f Control register +-- 15 ainc -/w/- enable address autoinc +-- 13:09 caddr -/w/- counter address +-- 07:00 vers r/-/- counter layout version +-- 02:00 func 0/-/f change run status if != noop +-- 0xx noop +-- 100 sto stop +-- 101 sta start +-- 110 clr clear +-- 111 loa load caddr +-- 01 stat r/-/- Status register +-- 15 ainc r/-/- enable address autoinc +-- 13:09 caddr r/-/- counter address +-- 08 waddr r/-/- word address +-- 00 run r/-/- running +-- 10 data r/-/- Data register +-- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.slvtypes.all; +use work.memlib.all; +use work.rblib.all; + +-- ---------------------------------------------------------------------------- + +entity pdp11_dmpcnt_precnt is -- pre-counter + port ( + CLK : in slbit; -- clock + CLR : in slbit; -- clear + ENA : in slbit; -- count + DOUT : out slv5 -- data + ); +end pdp11_dmpcnt_precnt; + +architecture syn of pdp11_dmpcnt_precnt is + signal R_CNT : slv5 := (others=>'0'); +begin + proc_cnt: process (CLK) + begin + + if rising_edge(CLK) then + if CLR = '1' then + R_CNT <= (others=>'0'); + else + if ENA = '1' then + R_CNT <= slv(unsigned(R_CNT) + 1); + end if; + end if; + end if; + + end process proc_cnt; + + DOUT <= R_CNT; + +end syn; + +-- ---------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.slvtypes.all; +use work.memlib.all; +use work.rblib.all; + +entity pdp11_dmpcnt is -- debug&moni: performance counters + generic ( + RB_ADDR : slv16 := slv(to_unsigned(16#0060#,16)); -- rbus address + VERS : slv8 := slv(to_unsigned(1, 8)); -- counter layout version + CENA : slv32 := (others=>'1')); -- counter enables + port ( + CLK : in slbit; -- clock + RESET : in slbit; -- reset + RB_MREQ : in rb_mreq_type; -- rbus: request + RB_SRES : out rb_sres_type; -- rbus: response + PERFSIG : in slv32 -- signals to count + ); +end pdp11_dmpcnt; + + +architecture syn of pdp11_dmpcnt is + + constant rbaddr_cntl : slv2 := "00"; -- cntl address offset + constant rbaddr_stat : slv2 := "01"; -- stat address offset + constant rbaddr_data : slv2 := "10"; -- data address offset + + constant cntl_rbf_ainc : integer := 15; + subtype cntl_rbf_caddr is integer range 13 downto 9; + subtype cntl_rbf_vers is integer range 7 downto 0; + subtype cntl_rbf_func is integer range 2 downto 0; + + constant stat_rbf_ainc : integer := 15; + subtype stat_rbf_caddr is integer range 13 downto 9; + constant stat_rbf_waddr : integer := 8; + constant stat_rbf_run : integer := 0; + + constant func_sto : slv3 := "100"; -- func: stop + constant func_sta : slv3 := "101"; -- func: start + constant func_clr : slv3 := "110"; -- func: clear + constant func_loa : slv3 := "111"; -- func: load + + type regs_type is record + rbsel : slbit; -- rbus select + run : slbit; -- run flag + saddr : slv5; -- scan address + raddr : slv5; -- read address (counter) + waddr : slbit; -- read address (word) + ainc : slbit; -- enable ddress autoinc + zbusy : slbit; -- clear in progress + dval : slbit; -- data valid + dout : slv32; -- read data (valid if dval=1) + psig : slv32; -- signals, floped + end record regs_type; + + constant regs_init : regs_type := ( + '0','0', -- rbsel,run + (others=>'0'), -- saddr + (others=>'0'), -- raddr + '0','0','0','0', -- waddr,ainc,zbusy,dval + (others=>'0'), -- dout + (others=>'0') -- psig + ); + + signal R_REGS : regs_type := regs_init; + signal N_REGS : regs_type; -- don't init (vivado fix for fsm infer) + + type pre_do_type is array (31 downto 0) of slv5; + + signal PRE_CLR : slv32 := (others=>'0'); + signal PRE_DO : pre_do_type := (others=> (others => '0')); + signal MEM_DI : slv32 := (others=>'0'); + signal MEM_DO : slv32 := (others=>'0'); + + begin + + MEM : ram_1swar_gen + generic map ( + AWIDTH => 5, + DWIDTH => 32) + port map ( + CLK => CLK, + WE => '1', + ADDR => R_REGS.saddr, + DI => MEM_DI, + DO => MEM_DO + ); + + PRE: for i in 31 downto 0 generate + ENA: if CENA(i)='1' generate + CNT : entity work.pdp11_dmpcnt_precnt + port map ( + CLK => CLK, + CLR => PRE_CLR(i), + ENA => R_REGS.psig(i), + DOUT => PRE_DO(i) + ); + end generate ENA; + end generate PRE; + + proc_cnt: process (R_REGS, PRE_DO, MEM_DO) + variable iclr : slv32 := (others=>'0'); + variable ipdo : slv32 := (others=>'0'); + variable icnt : slv32 := (others=>'0'); + variable imdi : slv32 := (others=>'0'); + constant ipdo_pad : slv(31 downto 5) := (others=>'0'); + constant icnt_pad : slv(31 downto 1) := (others=>'0'); + begin + iclr := (others=>'0'); + iclr(to_integer(unsigned(R_REGS.saddr))) := '1'; + + ipdo := ipdo_pad & PRE_DO(to_integer(unsigned(R_REGS.saddr))); + icnt := icnt_pad & R_REGS.psig(to_integer(unsigned(R_REGS.saddr))); + PRE_CLR <= iclr; + if R_REGS.zbusy = '0' then + imdi := slv(unsigned(MEM_DO) + unsigned(ipdo) + unsigned(icnt)); + else + imdi := (others=>'0'); + end if; + MEM_DI <= imdi; + + end process proc_cnt; + + proc_regs: process (CLK) + begin + + if rising_edge(CLK) then + if RESET = '1' then + R_REGS <= regs_init; + else + R_REGS <= N_REGS; + end if; + end if; + + end process proc_regs; + + proc_next: process (R_REGS, RB_MREQ, PERFSIG, MEM_DO) + + variable r : regs_type := regs_init; + variable n : regs_type := regs_init; + + variable irb_ack : slbit := '0'; + variable irb_busy : slbit := '0'; + variable irb_err : slbit := '0'; + variable irb_dout : slv16 := (others=>'0'); + variable irbena : slbit := '0'; + + begin + + r := R_REGS; + n := R_REGS; + + irb_ack := '0'; + irb_busy := '0'; + irb_err := '0'; + irb_dout := (others=>'0'); + irbena := RB_MREQ.re or RB_MREQ.we; + + -- rbus address decoder + n.rbsel := '0'; + if RB_MREQ.aval='1' then + if RB_MREQ.addr(15 downto 2)=RB_ADDR(15 downto 2) then + n.rbsel := '1'; + end if; + end if; + + if r.run = '1' then -- if running + n.psig := PERFSIG; -- capture performance signals + else + n.psig := (others=>'0'); -- otherwise ignore them + end if; + + n.saddr := slv(unsigned(r.saddr) + 1); -- scan counter (always running) + + -- capture data in dout buffer if scan=read address and looking at lsb and + -- if either data not valid or no rbus cycle active. this ensures that + -- dval waits end, and also that data isn't changing during rbus active. + if r.saddr = r.raddr and r.waddr = '0' and + (r.dval='0' or r.rbsel='0') then + n.dout := MEM_DO; -- capture data + n.dval := '1'; + end if; + + -- rbus transactions + if r.rbsel = '1' then + irb_ack := irbena; -- ack all accesses + case RB_MREQ.addr(1 downto 0) is + + when rbaddr_cntl => -- cntl ------------------ + if RB_MREQ.we = '1' then + case RB_MREQ.din(cntl_rbf_func) is + when func_sto => -- func: stop ------------ + n.run := '0'; + + when func_sta => -- func: start ----------- + n.run := '1'; + + when func_clr => -- func: clear ----------- + n.run := '0'; + if r.zbusy = '0' then + n.zbusy := '1'; + n.saddr := (others=>'0'); + n.raddr := (others=>'0'); + n.waddr := '0'; + n.ainc := '0'; + irb_busy := '1'; + else + if r.saddr = "11111" then + n.zbusy := '0'; + n.dval := '0'; + else + irb_busy := '1'; + end if; + end if; + + when func_loa => -- func: load ------------ + n.ainc := RB_MREQ.din(cntl_rbf_ainc); + n.raddr := RB_MREQ.din(cntl_rbf_caddr); + n.waddr := '0'; + n.dval := '0'; + + when others => null; -- <> -------------------- + end case; + end if; + + when rbaddr_stat => -- stat ------------------ + irb_err := RB_MREQ.we; + + when rbaddr_data => -- data ------------------ + -- write to data is an error + if RB_MREQ.we='1' then + irb_err := '1'; -- error + end if; + if RB_MREQ.re = '1' then + if r.dval = '0' then + irb_busy := '1'; + else + n.waddr := not r.waddr; + if r.ainc='1' and r.waddr = '1' then -- autoinc and wrap ? + n.raddr := slv(unsigned(r.raddr) + 1); + n.dval := '0'; + end if; + end if; + end if; + + when others => irb_err := '1'; -- <> -------------------- + end case; + end if; + + -- rbus output driver + if r.rbsel = '1' then + case RB_MREQ.addr(1 downto 0) is + + when rbaddr_cntl => null; -- cntl ------------------ + irb_dout(cntl_rbf_vers) := VERS; + + when rbaddr_stat => -- stat ------------------ + irb_dout(stat_rbf_ainc) := r.ainc; + irb_dout(stat_rbf_caddr) := r.raddr; + irb_dout(stat_rbf_waddr) := r.waddr; + irb_dout(stat_rbf_run) := r.run; + + when rbaddr_data => -- data ------------------ + if r.waddr = '0' then + irb_dout := r.dout(15 downto 0); + else + irb_dout := r.dout(31 downto 16); + end if; + + when others => null; + end case; + end if; + + N_REGS <= n; + + RB_SRES.ack <= irb_ack; + RB_SRES.err <= irb_err; + RB_SRES.busy <= irb_busy; + RB_SRES.dout <= irb_dout; + + end process proc_next; + +end syn;