From 5b8016f69c749cef8cc64cbe5a43ffbf0200fef0 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Sun, 15 Feb 2015 01:36:19 +0100 Subject: [PATCH] Include hardware design of FPGA peripherals to external LPC bus connection. Signed-off-by: Pavel Pisa --- hw/Makefile | 263 ++++++++++++++++++++++++++++ hw/bus_example.vhd | 38 ++++ hw/bus_measurement.vhd | 99 +++++++++++ hw/cnt_div.vhd | 60 +++++++ hw/dff2.vhd | 44 +++++ hw/dff3.vhd | 47 +++++ hw/lx-dad.ucf | 132 ++++++++++++++ hw/lx_crosdom_ser_fifo.vhd | 147 ++++++++++++++++ hw/lx_dad_pkg.vhd | 204 ++++++++++++++++++++++ hw/lx_dad_top.prj | 11 ++ hw/lx_dad_top.vhd | 338 ++++++++++++++++++++++++++++++++++++ hw/measurement_register.vhd | 71 ++++++++ hw/packager.c | 122 +++++++++++++ hw/util_pkg.vhd | 51 ++++++ hw/xilinx_dualport_bram.vhd | 170 ++++++++++++++++++ 15 files changed, 1797 insertions(+) create mode 100644 hw/Makefile create mode 100644 hw/bus_example.vhd create mode 100644 hw/bus_measurement.vhd create mode 100644 hw/cnt_div.vhd create mode 100644 hw/dff2.vhd create mode 100644 hw/dff3.vhd create mode 100644 hw/lx-dad.ucf create mode 100644 hw/lx_crosdom_ser_fifo.vhd create mode 100644 hw/lx_dad_pkg.vhd create mode 100644 hw/lx_dad_top.prj create mode 100644 hw/lx_dad_top.vhd create mode 100644 hw/measurement_register.vhd create mode 100644 hw/packager.c create mode 100644 hw/util_pkg.vhd create mode 100644 hw/xilinx_dualport_bram.vhd diff --git a/hw/Makefile b/hw/Makefile new file mode 100644 index 0000000..57d99f8 --- /dev/null +++ b/hw/Makefile @@ -0,0 +1,263 @@ +# TOP - Name of the top-level module +# DEVICE - Name of the FPGA device (device-package-speed) +# PRJ - Name of .prj file with names of all source files. See XST manual. +# UCF - Name of the user constraints file +# SEARCH_DIRS - Directories to search when searching for netlists (.ngc, ...). +# See NGDBUILD manual. +# INTSTYLE - Style of screen output. (ise | xflow | silent) + +# Targets desctiption: +# - synthesize : Synthesize all VHDL and Verilog source files, libraries, etc. +# defined in PRJ files and produces NGC file. +# - translate : Translate all netlist files (.ngc, ...) into the NGD file, +# where the design is described in terms of deneral logic elements +# such as (RAM, flip-flop, XOR, ...). +# - map : Map the general logic from NGD file to the components in the +# target FPGA and produces NCD_MAP file. +# - par : PAR stands for Plase & Route. This procedure takes NCD_MAP file, +# places all components and makes routes between them (depending +# on the chosen optimization mode) and produces NCD file. +# - clean : Clean build directory, dependency (*.d) files and call +# - gen : Transfer placed and routed NCD file into the bin file, which can +# be then used to configure particular FPGA (this is further packaged +# for SelectMap interface via the ARM CPU) +# - firmware : Builds the firmware for Tumbl core + +# Dependicies are handled, so in most cases only 'download' target is called. + +DEVICE := xc6slx9-2tqg144 + +TOP := lx_dad_top +OUT := _build +OUTB := lx-dad +REQB := $(OUT)/$(OUTB) +PRJ := lx_dad_top.prj +UCF := lx-dad.ucf +SEARCH_DIRS := ipcore_dir +INTSTYLE := xflow +SRC := .. + +#=============================================================================== +# Abbreviations of frequently used file names. + +NGC := $(OUTB).ngc +NGD := $(OUTB).ngd +PCF := $(OUTB).pcf +NCD_MAP := $(OUTB)_map.ncd +NCD := $(OUTB).ncd +BIN := $(OUTB).bin +PKG := $(OUTB).pkg +TWR := $(OUTB).twr +TSI := $(OUTB).tsi + +REQ_NGC := $(REQB).ngc +REQ_NGD := $(REQB).ngd +REQ_PCF := $(REQB).pcf +REQ_NCD_MAP := $(REQB)_map.ncd +REQ_NCD := $(REQB).ncd +REQ_BIN := $(REQB).bin +REQ_PKG := $(REQB).pkg +REQ_SRC := . + +#REQ_FIRMWARE := $(OUT)/imem.bin $(OUT)/imem.asm $(OUT)/dmem.bin $(OUT)/firmware.lst + +#=============================================================================== +# Sythesis settings (SmartXplorer) + +XST_GLOB_OPT := AllClockNets +XST_OPT_LEVEL := 2 +XST_OPT_MODE := Speed +XST_IOB_PACKING := False +XST_POWER := NO +XST_KEEP_HIEARCHY := No +XST_NETLIST_HIEARCHY := As_Optimized +XST_READ_CORES := YES +XST_WRITE_TIMING_CONSTRAINTS := NO +XST_CROSS_CLOCK_ANALYSIS := NO +XST_CASE := Maintain +XST_REDUCE_CONTROL_SETS := Auto +XST_REGISTER_DUPLICATION := YES +XST_REGISTER_BALANCING := Yes +XST_MOVE_FIRST_STAGE := YES +XST_MOVE_LAST_STAGE := YES +XST_OPTIMIZE_PRIMITIVES := NO +XST_USE_CLOCK_ENABLE := AUTO +XST_EQUIVALENT_REGISTER_REMOVAL := YES +XST_IOBUF := YES +XST_MAX_FANOUT := 100000 +XST_RESOURCE_SHARING := YES +XST_SLICE_UTILIZATION_RATIO_MARGIN := 5 +XST_SLICE_UTILIZATION_RATIO := 100 +XST_BRAM_UTILIZATION_RATIO := 100 +XST_DSP_UTILIZATION_RATIO := 100 +XST_USE_DSP48 := Auto +XST_USE_SYNC_SET := Auto +XST_USE_SYNC_RESET := Auto +XST_SAFE_IMPLEMENTATION := No + +MAP_PLACER_COST_TABLE := 2 +MAP_LOGIC_OPT := on +MAP_GLOBAL_OPT := off +MAP_EQUIVALENT_REGISTER_REMOVAL := off +MAP_LUT_COMBINING := off + +#=============================================================================== +# Firmware + +MB_CROSS_COMPILE ?= mbtumbl-elf- +TARGET_CC := $(MB_CROSS_COMPILE)gcc +TARGET_LD := $(MB_CROSS_COMPILE)ld +TARGET_OBJCOPY := $(MB_CROSS_COMPILE)objcopy +TARGET_OBJDUMP := $(MB_CROSS_COMPILE)objdump + +C_OBJS := $(OUT)/firmware.o +A_OBJS := +CFLAGS := -mxl-soft-div -msoft-float -Wno-main -Wl,-no-check-sections -ffunction-sections -fno-zero-initialized-in-bss -g -O2 -Wall +AFLAGS := -D__ASSEMBLY__ $(CFLAGS) +LDFLAGS := -static -nostdlib -relax -defsym _STACK_SIZE=0x0200 --gc-sections + +OBJS := $(OUT)/start.o $(C_OBJS) $(A_OBJS) + +FIRMWARE_DIR := ./lx-dad_firmware + +#=============================================================================== + +# Attempt to create a output directory. +$(shell [ -d ${OUT} ] || mkdir -p ${OUT}) + +# Verify if it was successful. +OUTPUT_DIR := $(shell cd $(OUT) && /bin/pwd) +$(if $(OUTPUT_DIR),,$(error output directory "$(OUT)" does not exist)) + +#=============================================================================== + +.PHONY: all +all: pkg firmware + +.PHONY: re-synthesize +re-synthesize $(REQ_NGC): $(addprefix $(REQ_SRC)/,$(PRJ)) + cd $(OUT); \ + echo " \ + run \ + $(addprefix -ifn $(SRC)/,$(PRJ)) \ + -ifmt mixed \ + -ofn $(NGC) \ + -ofmt NGC \ + -top $(TOP) \ + -p $(DEVICE) \ + -keep_hierarchy $(XST_KEEP_HIEARCHY) \ + -glob_opt $(XST_GLOB_OPT) \ + -opt_mode $(XST_OPT_MODE) \ + -opt_level $(XST_OPT_LEVEL) \ + -power $(XST_POWER) \ + -iob $(XST_IOB_PACKING) \ + -read_cores $(XST_READ_CORES) \ + -write_timing_constraints $(XST_WRITE_TIMING_CONSTRAINTS) \ + -cross_clock_analysis $(XST_CROSS_CLOCK_ANALYSIS) \ + -case $(XST_CASE) \ + -reduce_control_sets $(XST_REDUCE_CONTROL_SETS) \ + -resource_sharing $(XST_RESOURCE_SHARING) \ + -iobuf $(XST_IOBUF) \ + -max_fanout $(XST_MAX_FANOUT) \ + -register_duplication $(XST_REGISTER_DUPLICATION) \ + -register_balancing $(XST_REGISTER_BALANCING) \ + -move_first_stage $(XST_MOVE_FIRST_STAGE) \ + -move_last_stage $(XST_MOVE_LAST_STAGE) \ + -optimize_primitives $(XST_OPTIMIZE_PRIMITIVES) \ + -use_clock_enable $(XST_USE_CLOCK_ENABLE) \ + -equivalent_register_removal $(XST_EQUIVALENT_REGISTER_REMOVAL) \ + -slice_utilization_ratio_maxmargin $(XST_SLICE_UTILIZATION_RATIO_MARGIN) \ + -slice_utilization_ratio $(XST_SLICE_UTILIZATION_RATIO) \ + -bram_utilization_ratio $(XST_BRAM_UTILIZATION_RATIO) \ + -dsp_utilization_ratio $(XST_DSP_UTILIZATION_RATIO)" | xst | tee xst.log + +.PHONY: re-translate +re-translate $(REQ_NGD): $(REQ_NGC) $(REQ_UCF) + cd $(OUT); \ + ngdbuild -intstyle $(INTSTYLE) -p $(DEVICE) -nt timestamp -uc $(SRC)/$(UCF) \ + $(addprefix -sd $(SRC)/,$(SEARCH_DIRS)) \ + $(NGC) \ + $(NGD) + +.PHONY: re-map +re-map $(REQ_NCD_MAP) $(REQ_PCF): $(REQ_NGD) + cd $(OUT); \ + map -w -intstyle $(INTSTYLE) -p $(DEVICE) -logic_opt $(MAP_LOGIC_OPT) -ol high -t $(MAP_PLACER_COST_TABLE) -xt 0 \ + -r 4 -global_opt $(MAP_GLOBAL_OPT) -mt off -ir off -pr off -lc $(MAP_LUT_COMBINING) \ + -power off -equivalent_register_removal $(MAP_EQUIVALENT_REGISTER_REMOVAL) \ + -o $(NCD_MAP) $(NGD) $(PCF) | tee map.log + +.PHONY: re-par +re-par $(REQ_NCD): $(REQ_NCD_MAP) $(REQ_PCF) + cd $(OUT); \ + par -w -intstyle $(INTSTYLE) -ol high -xe n -mt off $(NCD_MAP) $(NCD) $(PCF) | tee par.log + cd $(OUT); \ + trce -e 20 -tsi $(TSI) -o $(TWR) $(NCD) $(PCF) + +.PHONY: re-gen +re-gen $(REQ_BIN): $(REQ_NCD) + cd $(OUT); \ + bitgen -w -g Binary:yes -g INIT_9K:Yes -g StartUpClk:Cclk $(NCD) $(OUTB) $(PCF) | tee bitgen.log + +.PHONY: packager +packager $(OUT)/packager: + gcc packager.c -o $(OUT)/packager + +.PHONY: re-pkg +re-pkg $(REQ_PKG): $(OUT)/packager $(REQ_BIN) + cd $(OUT); \ + ./packager le $(BIN) $(PKG) + +$(OUT)/%.o: $(FIRMWARE_DIR)/%.c + $(TARGET_CC) $(CFLAGS) -c $< -o $@ + +$(OUT)/%.o: $(FIRMWARE_DIR)/%.S + $(TARGET_CC) $(AFLAGS) -c $< -o $@ + +$(OUT)/firmware.elf: $(OBJS) + $(TARGET_LD) $(LDFLAGS) -T $(FIRMWARE_DIR)/utils/tumbl.ld-script -o $@ $(OBJS) + +$(OUT)/bin2mem: $(FIRMWARE_DIR)/utils/bin2mem.c + gcc $< -o $@ + +.PHONY: re-firmware +re-firmware $(REQ_FIRMWARE): $(REQ_PKG) $(OUT)/bin2mem $(OUT)/firmware.elf + $(TARGET_OBJCOPY) -O binary $(OUT)/firmware.elf -j .text -S $(OUT)/imem.bin + $(TARGET_OBJCOPY) -O binary $(OUT)/firmware.elf -j .data -S $(OUT)/dmem.bin + $(TARGET_OBJDUMP) -DSCz $(OUT)/firmware.elf >$(OUT)/firmware.lst + cd $(OUT); \ + $(TARGET_OBJDUMP) -b binary -mmbtumbl -EB -D imem.bin | sed -e 's/.data/.text/' > imem.asm + +# imem +# watch -d ./usb_sendhex -d 0x1669:0x1023 -t 4 -s 0x80000000 -l 0x200 -f dump -u - +# dmem +# watch -d ./usb_sendhex -d 0x1669:0x1023 -t 4 -s 0x80001000 -l 0x200 -f dump -u - +# PC +# watch -d ./usb_sendhex -d 0x1669:0x1023 -t 4 -s 0x80003008 -l 0x4 -f dump -u - + +#=============================================================================== + +.PHONY: clean +clean: + rm -rf $(OUT) + +.PHONY: synthesize +synthesize: $(REQ_NGC) + +.PHONY: translate +translate: $(REQ_NGD) + +.PHONY: map +map: $(REQ_NCD_MAP) $(REQ_PCF) + +.PHONY: par +par: $(REQ_NCD) + +.PHONY: gen +gen: $(REQ_BIN) + +.PHONY: pkg +pkg: $(OUT)/packager $(REQ_PKG) + +.PHONY: firmware +firmware: $(REQ_FIRMWARE) diff --git a/hw/bus_example.vhd b/hw/bus_example.vhd new file mode 100644 index 0000000..333c487 --- /dev/null +++ b/hw/bus_example.vhd @@ -0,0 +1,38 @@ +library ieee; + +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.lx_dad_pkg.all; + +-- Connects example memory + +entity bus_example is + port + ( + -- Clock + clk_i : in std_logic; + -- Chip enable + ce_i : in std_logic; + -- Global Reset + reset_i : in std_logic; + -- Master CPU peripheral bus + bls_i : in std_logic_vector(3 downto 0); + address_i : in std_logic_vector(11 downto 0); + data_i : in std_logic_vector(31 downto 0); + data_o : out std_logic_vector(31 downto 0) + + -- Non bus signals + -- + -- Add there external component signals + ); +end bus_example; + +architecture Behavioral of bus_example is + +begin + + data_o <= (others => '0'); + +end Behavioral; diff --git a/hw/bus_measurement.vhd b/hw/bus_measurement.vhd new file mode 100644 index 0000000..b10f1ac --- /dev/null +++ b/hw/bus_measurement.vhd @@ -0,0 +1,99 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.lx_dad_pkg.all; + +-- Memory bus measurement +-- Holds the signal for one clock to simulate longest route + +entity bus_measurement is + port + ( + -- Clock + clk_i : in std_logic; + -- Reset + reset_i : in std_logic; + -- Chip enable + ce_i : in std_logic; + -- Address + address_i : in std_logic_vector(1 downto 0); + -- Data bus + data_i : in std_logic_vector(31 downto 0); + data_o : out std_logic_vector(31 downto 0); + -- Bus signals + bls_i : in std_logic_vector(3 downto 0) + ); +end bus_measurement; + +architecture Behavioral of bus_measurement is + + -- Wiring + signal meas1_out_s : std_logic_vector(31 downto 0); + signal meas1_ce_s : std_logic; + -- + signal meas2_out_s : std_logic_vector(31 downto 0); + signal meas2_ce_s : std_logic; + +begin + + -- First measurement register (0xAAAAAAAA) +measurement1: measurement_register + generic map + ( + id_g => "10101010101010101010101010101010" + ) + port map + ( + clk_i => clk_i, + ce_i => meas1_ce_s, + switch_i => address_i(0), + reset_i => reset_i, + bls_i => bls_i, + data_i => data_i, + data_o => meas1_out_s + ); + + -- Second measurement register (=0x55555555) +measurement2: measurement_register + generic map + ( + id_g => "01010101010101010101010101010101" + ) + port map + ( + clk_i => clk_i, + ce_i => meas2_ce_s, + switch_i => address_i(0), + reset_i => reset_i, + bls_i => bls_i, + data_i => data_i, + data_o => meas2_out_s + ); + +-- Bus process +update: + process (ce_i, address_i, meas1_out_s, meas2_out_s) + begin + + -- Defaults + meas1_ce_s <= '0'; + meas2_ce_s <= '0'; + data_o <= (others => 'X'); + + -- Chip Enable + if ce_i = '1' then + if address_i(1) = '0' then + meas1_ce_s <= '1'; + data_o <= meas1_out_s; + else + meas2_ce_s <= '1'; + data_o <= meas2_out_s; + end if; + end if; + + end process; + +end Behavioral; + diff --git a/hw/cnt_div.vhd b/hw/cnt_div.vhd new file mode 100644 index 0000000..79c4cce --- /dev/null +++ b/hw/cnt_div.vhd @@ -0,0 +1,60 @@ +-- +-- * Counter - divider * +-- +-- part of LXPWR motion control board (c) PiKRON Ltd +-- idea by Pavel Pisa PiKRON Ltd +-- +-- license: BSD +-- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity cnt_div is + generic ( + cnt_width_g : natural := 8 + ); + port + ( + clk_i : in std_logic; + en_i : in std_logic; + reset_i : in std_logic; + ratio_i : in std_logic_vector(cnt_width_g-1 downto 0); + q_out_o : out std_logic + ); +end cnt_div; + +architecture behavioral of cnt_div is + signal cnt_val_s : natural range 0 to (2**cnt_width_g - 1); + signal cnt_val_r : natural range 0 to (2**cnt_width_g - 1); +begin + +comb: process (reset_i, en_i, ratio_i, cnt_val_r) + begin + if reset_i = '1' then + cnt_val_s <= to_integer(unsigned(ratio_i)); + q_out_o <= '0'; + else + if en_i = '0' then + cnt_val_s <= cnt_val_r; + q_out_o <= '0'; + else + if cnt_val_r <= 1 then + cnt_val_s <= to_integer(unsigned(ratio_i)); + q_out_o <= '1'; + else + cnt_val_s <= cnt_val_r - 1; + q_out_o <= '0'; + end if; + end if; + end if; + end process; + +seq: process + begin + wait until clk_i'event and clk_i = '1'; + cnt_val_r <= cnt_val_s; + end process; + +end behavioral; diff --git a/hw/dff2.vhd b/hw/dff2.vhd new file mode 100644 index 0000000..5560dd1 --- /dev/null +++ b/hw/dff2.vhd @@ -0,0 +1,44 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.lx_dad_pkg.all; + +-- D circuit (filtered) + +entity dff2 is + port + ( + clk_i : in std_logic; + d_i : in std_logic; + q_o : out std_logic + ); +end dff2; + +architecture behavioral of dff2 is + signal d_2r : std_logic; + signal d_r : std_logic; + signal data_s : std_logic; + + -- XST attributes + attribute REGISTER_DUPLICATION : string; + attribute REGISTER_DUPLICATION of d_2r : signal is "NO"; + attribute REGISTER_DUPLICATION of d_r : signal is "NO"; + +begin + q_o <= data_s; + +seq: + process + begin + wait until clk_i'event and clk_i = '1'; + if d_2r = d_r then + data_s <= d_r; + end if; + + d_2r <= d_r; + d_r <= d_i; + end process; + +end behavioral; diff --git a/hw/dff3.vhd b/hw/dff3.vhd new file mode 100644 index 0000000..7e31e59 --- /dev/null +++ b/hw/dff3.vhd @@ -0,0 +1,47 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.lx_dad_pkg.all; + +-- D circuit (filtered) + +entity dff3 is + port + ( + clk_i : in std_logic; + d_i : in std_logic; + q_o : out std_logic + ); +end dff3; + +architecture behavioral of dff3 is + signal d_3r : std_logic; + signal d_2r : std_logic; + signal d_r : std_logic; + signal data_s : std_logic; + + -- XST attributes + attribute REGISTER_DUPLICATION : string; + attribute REGISTER_DUPLICATION of d_3r : signal is "NO"; + attribute REGISTER_DUPLICATION of d_2r : signal is "NO"; + attribute REGISTER_DUPLICATION of d_r : signal is "NO"; + +begin + q_o <= data_s; + +seq: + process + begin + wait until clk_i'event and clk_i = '1'; + if d_3r = d_2r and d_2r = d_r then + data_s <= d_3r; + end if; + + d_3r <= d_2r; + d_2r <= d_r; + d_r <= d_i; + end process; + +end behavioral; diff --git a/hw/lx-dad.ucf b/hw/lx-dad.ucf new file mode 100644 index 0000000..a7baf9a --- /dev/null +++ b/hw/lx-dad.ucf @@ -0,0 +1,132 @@ +# lx-dad with LPC4088 and Spartan6 XC6SLX9-2TQG144 +CONFIG VCCAUX = 3.3; + +# ==================================================================== +# INPUTS +# ==================================================================== + +# Clock +#NET CLK_CPU PERIOD = 13.88ns HIGH 50%; +#NET CLK_CPU LOC = P15 | IOSTANDARD = LVCMOS33 | SLEW = FAST; +NET CLK_50M PERIOD = 20.00ns HIGH 50%; +NET CLK_50M LOC = P14 | IOSTANDARD = LVCMOS33 | SLEW = FAST; + +# Reset (active LOW) +NET INIT LOC = P39 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; + +# Memory peripheral +NET CS0_XC LOC = P64 | IOSTANDARD = LVCMOS33 | SLEW = FAST; +#NET CS1_XC LOC = P1 | IOSTANDARD = LVCMOS33 | SLEW = FAST; + +NET RD LOC = P60 | IOSTANDARD = LVCMOS33 | SLEW = FAST; +NET BLS<0> LOC = P70 | IOSTANDARD = LVCMOS33 | SLEW = FAST; +NET BLS<1> LOC = P67 | IOSTANDARD = LVCMOS33 | SLEW = FAST; +NET BLS<2> LOC = P66 | IOSTANDARD = LVCMOS33 | SLEW = FAST; +NET BLS<3> LOC = P55 | IOSTANDARD = LVCMOS33 | SLEW = FAST; + +NET ADDRESS<0> LOC = P134 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<1> LOC = P133 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<2> LOC = P132 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<3> LOC = P131 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<4> LOC = P127 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<5> LOC = P126 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<6> LOC = P124 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<7> LOC = P123 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<8> LOC = P121 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<9> LOC = P120 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<10> LOC = P119 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<11> LOC = P118 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<12> LOC = P117 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<13> LOC = P116 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<14> LOC = P115 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET ADDRESS<15> LOC = P114 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; + +NET DATA<0> LOC = P65 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<1> LOC = P62 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<2> LOC = P61 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<3> LOC = P46 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<4> LOC = P45 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<5> LOC = P44 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<6> LOC = P43 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<7> LOC = P48 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<8> LOC = P41 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<9> LOC = P40 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<10> LOC = P59 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<11> LOC = P58 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<12> LOC = P57 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<13> LOC = P56 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<14> LOC = P51 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<15> LOC = P50 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<16> LOC = P38 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<17> LOC = P35 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<18> LOC = P34 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<19> LOC = P33 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<20> LOC = P32 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<21> LOC = P30 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<22> LOC = P29 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<23> LOC = P27 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<24> LOC = P144 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<25> LOC = P143 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<26> LOC = P142 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<27> LOC = P141 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<28> LOC = P140 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<29> LOC = P139 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<30> LOC = P138 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +NET DATA<31> LOC = P137 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; + +# Interrupt to master MCU +NET EVENT_JK_J LOC = P111 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; + +# IRC +#NET IRC0_A LOC = P2 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC0_B LOC = P6 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC0_INDEX LOC = P7 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC0_MARK LOC = P5 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +#NET IRC1_A LOC = P8 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC1_B LOC = P10 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC1_INDEX LOC = P11 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC1_MARK LOC = P9 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +#NET IRC2_A LOC = P12 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC2_B LOC = P17 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC2_INDEX LOC = P21 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC2_MARK LOC = P16 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +#NET IRC3_A LOC = P22 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC3_B LOC = P24 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC3_INDEX LOC = P26 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC3_MARK LOC = P23 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +#NET IRC4_A LOC = P93 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC4_B LOC = P104 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC4_INDEX LOC = P105 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC4_MARK LOC = P92 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +#NET IRC5_A LOC = P85 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC5_B LOC = P87 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC5_INDEX LOC = P88 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC5_MARK LOC = P84 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +#NET IRC6_A LOC = P81 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC6_B LOC = P82 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC6_INDEX LOC = P83 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC6_MARK LOC = P80 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +#NET IRC7_A LOC = P75 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC7_B LOC = P78 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC7_INDEX LOC = P79 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET IRC7_MARK LOC = P74 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; + +# ==================================================================== +# S1 Interface +# ==================================================================== + +#NET S1_CLK_IN PERIOD = 20.00ns HIGH 50%; +#NET S1_CLK_IN LOC = P95 | IOSTANDARD = LVCMOS33 | SLEW = FAST | PULLUP; +#NET S1_MISO LOC = P98 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | PULLUP; +#NET S1_SYNC_IN LOC = P100 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | PULLUP; + +#NET S1_CLK_OUT LOC = P94 | IOSTANDARD = LVCMOS33 | SLEW = FAST; +#NET S1_MOSI LOC = P97 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; +#NET S1_SYNC_OUT LOC = P101 | IOSTANDARD = LVCMOS33 | SLEW = SLOW; diff --git a/hw/lx_crosdom_ser_fifo.vhd b/hw/lx_crosdom_ser_fifo.vhd new file mode 100644 index 0000000..49846c5 --- /dev/null +++ b/hw/lx_crosdom_ser_fifo.vhd @@ -0,0 +1,147 @@ +-- Clock Cross Domain Synchronization Elastic Buffer/FIFO +-- +-- Copyright (c) 2014, Pavel Pisa +-- Designed for PiKRON company robotic controller +-- All rights reserved. +-- +-- 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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. +-- +-- Can be used and distributed under GPLv3 license as well + +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.util_pkg.all; +use work.lx_dad_pkg.all; + +entity lx_crosdom_ser_fifo is +generic +( + fifo_len_g : positive := 8; + sync_adj_g : integer := 0 +); +port +( + -- Asynchronous clock domain interface + acd_clock_i : in std_logic; + acd_miso_i : in std_logic; + acd_sync_i : in std_logic; + -- Clock + clk_i : in std_logic; + reset_i : in std_logic; + -- Output synchronous with clk_i + miso_o : out std_logic; + sync_o : out std_logic; + data_ready_o : out std_logic +); +end lx_crosdom_ser_fifo; + +architecture Behavioral of lx_crosdom_ser_fifo is + signal fifo_bits_s : std_logic_vector(0 to fifo_len_g - 1); + signal fifo_bits_r : std_logic_vector(0 to fifo_len_g - 1); + + signal acd_miso_r : std_logic; + signal acd_sync_r : std_logic; + signal acd_sync_prev_s : std_logic; + signal acd_sync_prev_r : std_logic; + + signal acd_in_loc_s : natural range 0 to fifo_len_g - 1; + signal acd_in_loc_r : natural range 0 to fifo_len_g - 1; + + signal out_loc_s : natural range 0 to fifo_len_g - 1; + signal out_loc_r : natural range 0 to fifo_len_g - 1; + + signal out_sync_s : std_logic_vector(0 to fifo_len_g / 2 - 1); + signal out_sync_r : std_logic_vector(0 to fifo_len_g / 2 - 1); + + signal out_miso_s : std_logic; + + attribute REGISTER_DUPLICATION : string; + attribute REGISTER_DUPLICATION of fifo_bits_s : signal is "NO"; + attribute REGISTER_DUPLICATION of fifo_bits_r : signal is "NO"; + attribute REGISTER_DUPLICATION of acd_sync_r : signal is "NO"; + attribute REGISTER_DUPLICATION of out_sync_s : signal is "NO"; + attribute REGISTER_DUPLICATION of out_sync_r : signal is "NO"; + +begin + sync_o <= out_sync_r(max(-sync_adj_g, 0)); + + data_ready_o <= '0'; + +acd_logic: + process (acd_miso_r, acd_sync_r, acd_sync_prev_r, acd_in_loc_r, fifo_bits_r) + begin + acd_sync_prev_s <= acd_sync_r; + fifo_bits_s <= fifo_bits_r; + if (acd_sync_r = '1') and (acd_sync_prev_r = '0') then + acd_in_loc_s <= 0; + fifo_bits_s(0) <= acd_miso_r; + else + fifo_bits_s(acd_in_loc_r) <= acd_miso_r; + if acd_in_loc_r /= fifo_len_g - 1 then + acd_in_loc_s <= acd_in_loc_r + 1; + else + acd_in_loc_s <= 0; + end if; + end if; + end process; + +acd_update: + process + begin + wait until acd_clock_i'event and acd_clock_i = '1'; + + acd_miso_r <= acd_miso_i; + acd_sync_r <= acd_sync_i; + acd_sync_prev_r <= acd_sync_prev_s; + acd_in_loc_r <= acd_in_loc_s; + fifo_bits_r <= fifo_bits_s; + end process; + +sync_logic: + process (fifo_bits_r, out_loc_r, out_sync_r, acd_sync_r) + begin + out_sync_s <= out_sync_r(1 to out_sync_r'length - 1 ) & acd_sync_r; + if out_sync_r(max(sync_adj_g, 0)) = '0' then + out_loc_s <= 0; + out_miso_s <= '0'; + else + out_miso_s <= fifo_bits_r(out_loc_r); + if out_loc_r /= fifo_len_g - 1 then + out_loc_s <= out_loc_r + 1; + else + out_loc_s <= 0; + end if; + end if; + end process; + +sync_update: + process + begin + wait until clk_i'event and clk_i = '1'; + + out_loc_r <= out_loc_s; + miso_o <= out_miso_s; + out_sync_r <= out_sync_s; + end process; + +end Behavioral; diff --git a/hw/lx_dad_pkg.vhd b/hw/lx_dad_pkg.vhd new file mode 100644 index 0000000..de69d2c --- /dev/null +++ b/hw/lx_dad_pkg.vhd @@ -0,0 +1,204 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.util_pkg.all; + +-- Entities within lx_dad + +package lx_dad_pkg is + + -- D sampler (filtered, 2 cycles) + component dff2 + port + ( + clk_i : in std_logic; + d_i : in std_logic; + q_o : out std_logic + ); + end component; + + -- D sampler (filtered, 3 cycles) + component dff3 + port + ( + clk_i : in std_logic; + d_i : in std_logic; + q_o : out std_logic + ); + end component; + + -- Counter - divider + component cnt_div + generic ( + cnt_width_g : natural := 8 + ); + port + ( + clk_i : in std_logic; + en_i : in std_logic; + reset_i : in std_logic; + ratio_i : in std_logic_vector(cnt_width_g-1 downto 0); + q_out_o : out std_logic + ); + end component; + + -- Clock Cross Domain Synchronization Elastic Buffer/FIFO + component lx_crosdom_ser_fifo + generic + ( + fifo_len_g : positive := 8; + sync_adj_g : integer := 0 + ); + port + ( + -- Asynchronous clock domain interface + acd_clock_i : in std_logic; + acd_miso_i : in std_logic; + acd_sync_i : in std_logic; + -- Clock + clk_i : in std_logic; + reset_i : in std_logic; + -- Output synchronous with clk_i + miso_o : out std_logic; + sync_o : out std_logic; + data_ready_o : out std_logic + ); + end component; + + -------------------------------------------------------------------------------- + -- MEMORY BUS + -------------------------------------------------------------------------------- + + -- Measurement register + component measurement_register + generic + ( + id_g : std_logic_vector(31 downto 0) := (others => '0') + ); + port + ( + -- Clock + clk_i : in std_logic; + -- Reset + reset_i : in std_logic; + -- Chip enable + ce_i : in std_logic; + -- Switch + switch_i : in std_logic; + -- Data bus + data_i : in std_logic_vector(31 downto 0); + data_o : out std_logic_vector(31 downto 0); + -- Bus signals + bls_i : in std_logic_vector(3 downto 0) + ); + end component; + + -- Example component interconnect + component bus_example + port + ( + clk_i : in std_logic; + reset_i : in std_logic; + -- Master CPU peripheral bus + address_i : in std_logic_vector(11 downto 0); + ce_i : in std_logic; + data_i : in std_logic_vector(31 downto 0); + data_o : out std_logic_vector(31 downto 0); + -- + bls_i : in std_logic_vector(3 downto 0) + + -- Non bus signals + -- + -- Add there externaly visible signals + ); + end component; + + -- Measurement interconnect + component bus_measurement + port + ( + -- Clock + clk_i : in std_logic; + -- Reset + reset_i : in std_logic; + -- Chip enable + ce_i : in std_logic; + -- Address + address_i : in std_logic_vector(1 downto 0); + -- Data bus + data_i : in std_logic_vector(31 downto 0); + data_o : out std_logic_vector(31 downto 0); + -- Bus signals + bls_i : in std_logic_vector(3 downto 0) + ); + end component; + + -- Register on the bus + component bus_register is + generic + ( + -- Reset value + reset_value_g : std_logic_vector(31 downto 0) := (others => '0'); + -- Width + b0_g : natural := 8; + b1_g : natural := 8; + b2_g : natural := 8; + b3_g : natural := 8 + ); + port + ( + -- Clock + clk_i : in std_logic; + -- Reset + reset_i : in std_logic; + -- Chip enable + ce_i : in std_logic; + -- Data bus + data_i : in std_logic_vector((b0_g+b1_g+b2_g+b3_g-1) downto 0); + data_o : out std_logic_vector((b0_g+b1_g+b2_g+b3_g-1) downto 0); + -- Bus signals + bls_i : in std_logic_vector(3 downto 0) + ); + end component; + + + -------------------------------------------------------------------------------- + -- BRAM + -------------------------------------------------------------------------------- + type BRAM_type is (READ_FIRST, WRITE_FIRST, NO_CHANGE); + + component xilinx_dualport_bram + generic + ( + byte_width : positive := 8; + address_width : positive := 8; + we_width : positive := 4; + port_a_type : BRAM_type := READ_FIRST; + port_b_type : BRAM_type := READ_FIRST + ); + port + ( + clka : in std_logic; + rsta : in std_logic; + ena : in std_logic; + wea : in std_logic_vector((we_width-1) downto 0); + addra : in std_logic_vector((address_width-1) downto 0); + dina : in std_logic_vector(((byte_width*we_width)-1) downto 0); + douta : out std_logic_vector(((byte_width*we_width)-1) downto 0); + clkb : in std_logic; + rstb : in std_logic; + enb : in std_logic; + web : in std_logic_vector((we_width-1) downto 0); + addrb : in std_logic_vector((address_width-1) downto 0); + dinb : in std_logic_vector(((byte_width*we_width)-1) downto 0); + doutb : out std_logic_vector(((byte_width*we_width)-1) downto 0) + ); + end component; + +end lx_dad_pkg; + +package body lx_dad_pkg is + +end lx_dad_pkg; diff --git a/hw/lx_dad_top.prj b/hw/lx_dad_top.prj new file mode 100644 index 0000000..5693fcb --- /dev/null +++ b/hw/lx_dad_top.prj @@ -0,0 +1,11 @@ +vhdl work "util_pkg.vhd" +vhdl work "lx_dad_pkg.vhd" +vhdl work "xilinx_dualport_bram.vhd" +vhdl work "dff3.vhd" +vhdl work "dff2.vhd" +vhdl work "cnt_div.vhd" +vhdl work "measurement_register.vhd" +vhdl work "lx_crosdom_ser_fifo.vhd" +vhdl work "bus_measurement.vhd" +vhdl work "bus_example.vhd" +vhdl work "lx_dad_top.vhd" diff --git a/hw/lx_dad_top.vhd b/hw/lx_dad_top.vhd new file mode 100644 index 0000000..0905804 --- /dev/null +++ b/hw/lx_dad_top.vhd @@ -0,0 +1,338 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +use work.lx_dad_pkg.all; + +-- lx_dad_top - wires the modules with the outside world + +-- ====================================================== +-- MASTER CPU EXTERNAL MEMORY BUS +-- ====================================================== +-- +-- Master cpu memory bus has the following wires: +-- +-- - address[15..0] The address, used to mark chip enable +-- - data_in[31..0] The data coming to bus +-- - data_out[31..0] The data coming from bus, multiplexed +-- - bls[3..0] Write enable for respective bytes + +entity lx_dad_top is + port + ( + -- External + --clk_cpu : in std_logic; + clk_50m : in std_logic; + -- + cs0_xc : in std_logic; + -- + rd : in std_logic; + bls : in std_logic_vector(3 downto 0); + address : in std_logic_vector(15 downto 0); + data : inout std_logic_vector(31 downto 0); + -- + init : in std_logic; + -- signal connected to external JK FF + event_jk_j : out std_logic + ); +end lx_dad_top; + +architecture Behavioral of lx_dad_top is + + -- Reset signal + signal reset_s : std_logic; + signal init_s : std_logic; + -- Peripherals on the memory buses + -- Example memory + signal example_out_s : std_logic_vector(31 downto 0); + signal example_ce_s : std_logic; + -- Measurement (Master) + signal meas_out_s : std_logic_vector(31 downto 0); + signal meas_ce_s : std_logic; + -- Signals for external bus transmission + signal data_i_s : std_logic_vector(31 downto 0); + signal data_o_s : std_logic_vector(31 downto 0); + -- Signals for internal transaction + signal last_address_s : std_logic_vector(15 downto 0); + signal next_last_address_s : std_logic_vector(15 downto 0); + signal next_address_hold_s : std_logic; + signal address_hold_s : std_logic; + signal last_rd_s : std_logic; + signal next_last_rd_s : std_logic; + signal last_bls_s : std_logic_vector(3 downto 0); -- prev bls_f_s (active 1) + signal next_last_bls_s : std_logic_vector(3 downto 0); + + -- Reading logic for Master CPU: + -- Broadcast rd only till ta (transaction acknowledge) + -- is received, then latch the data till the state of + -- rd or address changes + -- + -- Data latching is synchronous - it's purpose is to + -- provide stable data for CPU on the bus + signal cs0_xc_f_s : std_logic; + signal rd_f_s : std_logic; -- Filtered RD + signal i_rd_s : std_logic; -- Internal bus RD (active 1) + -- signal next_i_rd_s : std_logic; + signal last_i_rd_s : std_logic; -- Delayed RD bus, used for latching + signal next_last_i_rd_s : std_logic; + signal i_rd_cycle2_s : std_logic; -- Some internal subsystems provide + signal next_i_rd_cycle2_s : std_logic; -- data only after 2 cycles + -- + signal address_f_s : std_logic_vector(15 downto 0); -- Filtered address + -- + signal data_f_s : std_logic_vector(31 downto 0); -- Filterred input data + -- + signal data_read_s : std_logic_vector(31 downto 0); -- Latched read data + signal next_data_read_s : std_logic_vector(31 downto 0); + + -- Writing logic: + signal bls_f_s : std_logic_vector(3 downto 0); -- Filtered BLS (active 1) + signal i_bls_s : std_logic_vector(3 downto 0); -- Internal BLS (active 1) + signal next_i_bls_s : std_logic_vector(3 downto 0); + -- + signal data_write_s : std_logic_vector(31 downto 0); -- Data broadcasted to write + signal next_data_write_s : std_logic_vector(31 downto 0); + + -- signal s0 : std_logic; + -- signal s1 : std_logic; + -- signal s2 : std_logic; + + -- XST attributes + attribute REGISTER_DUPLICATION : string; + attribute REGISTER_DUPLICATION of rd : signal is "NO"; + attribute REGISTER_DUPLICATION of rd_f_s : signal is "NO"; + attribute REGISTER_DUPLICATION of bls : signal is "NO"; + attribute REGISTER_DUPLICATION of bls_f_s : signal is "NO"; + attribute REGISTER_DUPLICATION of address : signal is "NO"; + attribute REGISTER_DUPLICATION of address_f_s : signal is "NO"; + attribute REGISTER_DUPLICATION of cs0_xc : signal is "NO"; + attribute REGISTER_DUPLICATION of cs0_xc_f_s : signal is "NO"; + +begin + +-- Example connection +memory_bus_example: bus_example + port map + ( + clk_i => clk_50m, + reset_i => reset_s, + ce_i => example_ce_s, + bls_i => i_bls_s, + address_i => address_f_s(11 downto 0), + data_i => data_i_s, + data_o => example_out_s + -- + -- + -- additional externally connected signals goes there + ); + +-- Measurement +memory_bus_measurement: bus_measurement + port map + ( + clk_i => clk_50m, + reset_i => reset_s, + ce_i => meas_ce_s, + address_i => address_f_s(1 downto 0), + bls_i => i_bls_s, + data_i => data_i_s, + data_o => meas_out_s + ); + +-- Reset +dff_reset: dff2 + port map + ( + clk_i => clk_50m, + d_i => init_s, + q_o => reset_s + ); + + -- Reset + init_s <= not init; + + -- Signalling + data_i_s <= data_write_s; + + + event_jk_j <= '0'; + +-- Bus update +memory_bus_logic: + process(cs0_xc_f_s, rd_f_s, last_rd_s, i_rd_cycle2_s, last_i_rd_s, + bls_f_s, last_bls_s, data_f_s, data_write_s, + data_o_s, data_read_s, last_address_s, address_f_s) + begin + -- Defaults + next_i_rd_cycle2_s <= '0'; + next_address_hold_s <= '0'; + + -- Check if we have chip select + if cs0_xc_f_s = '1' then + + -- Reading + if rd_f_s = '1' then + -- Internal read + if last_rd_s = '0' or (last_address_s /= address_f_s) then + i_rd_s <= '1'; + next_i_rd_cycle2_s <= '1'; + next_last_i_rd_s <= '1'; + elsif i_rd_cycle2_s = '1' then -- FIXME it seems that some internal + i_rd_s <= '1'; -- peripherals demands 2 cycles to read + next_last_i_rd_s <= '1'; + else + i_rd_s <= '0'; + next_last_i_rd_s <= '0'; + end if; + + if last_i_rd_s = '1' then + -- Latch data we just read - they are valid in this cycle + next_data_read_s <= data_o_s; + else + next_data_read_s <= data_read_s; + end if; + else + -- -- Not reading, anything goes + -- data_read_s <= (others => 'X'); + next_data_read_s <= data_read_s; + i_rd_s <= '0'; + next_last_i_rd_s <= '0'; + end if; + + next_last_rd_s <= rd_f_s; + + -- Data for write are captured only when BLS signals are stable + if bls_f_s /= "0000" then + next_data_write_s <= data_f_s; + next_address_hold_s <= '1'; + else + next_data_write_s <= data_write_s; + end if; + + if (bls_f_s /= "0000") or (rd_f_s = '1') then + next_last_address_s <= address_f_s; + else + next_last_address_s <= last_address_s; + end if; + else + next_last_rd_s <= '0'; + i_rd_s <= '0'; + next_last_i_rd_s <= '0'; + + next_i_bls_s <= "0000"; + next_data_write_s <= data_write_s; + next_data_read_s <= data_read_s; + next_last_address_s <= last_address_s; + end if; + + -- Data for write are captured at/before BLS signals are negated + -- and actual write cycle takes place exacly after BLS negation + if ((last_bls_s and not bls_f_s) /= "0000") or + ((last_bls_s /= "0000") and (cs0_xc_f_s = '0')) then + next_i_bls_s <= last_bls_s; + next_last_bls_s <= "0000"; + next_address_hold_s <= '1'; + else + next_i_bls_s <= "0000"; + if cs0_xc_f_s = '1' then + next_last_bls_s <= bls_f_s; + else + next_last_bls_s <= "0000" ; + end if; + end if; + + end process; + +-- Bus update +memory_bus_update: + process + begin + + wait until clk_50m = '1' and clk_50m'event; + + address_hold_s <= next_address_hold_s; + + -- Synchronized external signals with main clock domain + cs0_xc_f_s <= not cs0_xc; + bls_f_s <= not bls; + rd_f_s <= not rd; + data_f_s <= data; + if address_hold_s = '0' then + address_f_s <= address; + else + address_f_s <= next_last_address_s; + end if; + + -- Synchronoust state andvance to next period + last_bls_s <= next_last_bls_s; + last_rd_s <= next_last_rd_s; + i_bls_s <= next_i_bls_s; + -- i_rd_s <= next_i_rd_s; + i_rd_cycle2_s <= next_i_rd_cycle2_s; + last_i_rd_s <= next_last_i_rd_s; + data_write_s <= next_data_write_s; + last_address_s <= next_last_address_s; + data_read_s <= next_data_read_s; + + end process; + +-- Do the actual wiring here +memory_bus_wiring: + process(cs0_xc_f_s, i_bls_s, address_f_s, example_out_s, meas_out_s) + begin + + -- Inactive by default + example_ce_s <= '0'; + meas_ce_s <= '0'; + data_o_s <= (others => '0'); + + if cs0_xc_f_s = '1' or i_bls_s /= "0000" then + + -- Memory Map (16-bit address @ 32-bit each) + + -- Each address is seen as 32-bit entry now + -- 0x0000 - 0x0FFF: Example memory + -- 0x1FFC - 0x1FFF: Measurement + -- 0x2000 - 0x8FFF: Free space + + if address_f_s < "0001000000000000" then -- Tumbl + example_ce_s <= '1'; + data_o_s <= example_out_s; + elsif address_f_s(15 downto 2) = "00011111111111" then -- Measurement + meas_ce_s <= '1'; + data_o_s <= meas_out_s; + end if; + + end if; + + end process; + +-- If RD and BLS is not high, we must keep DATA at high impedance +-- or the FPGA collides with SDRAM (damaging each other) +memory_bus_out: + process(cs0_xc, rd, data_read_s) + begin + + -- CS0 / RD / BLS are active LOW + if cs0_xc = '0' and rd = '0' then + -- Don't risk flipping (between data_o_s and latched data_read_s, it's better to wait) + -- Maybe check this later. + -- if last_i_rd_s = '1' then + -- data <= data_o_s; + -- else + data <= data_read_s; + -- end if; + else + -- IMPORTANT!!! + data <= (others => 'Z'); + end if; + + end process; + +end Behavioral; + diff --git a/hw/measurement_register.vhd b/hw/measurement_register.vhd new file mode 100644 index 0000000..8cfd103 --- /dev/null +++ b/hw/measurement_register.vhd @@ -0,0 +1,71 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.lx_dad_pkg.all; + +-- Transaction measurement register + +entity measurement_register is + generic + ( + id_g : std_logic_vector(31 downto 0) := (others => '0') + ); + port + ( + -- Clock + clk_i : in std_logic; + -- Reset + reset_i : in std_logic; + -- Chip enable + ce_i : in std_logic; + -- Switch + switch_i : in std_logic; + -- Data bus + data_i : in std_logic_vector(31 downto 0); + data_o : out std_logic_vector(31 downto 0); + -- Bus signals + bls_i : in std_logic_vector(3 downto 0) + ); +end measurement_register; + +architecture Behavioral of measurement_register is + signal value_s : std_logic_vector(31 downto 0); +begin + + data_o <= value_s when switch_i = '1' else id_g; + +-- Write waits for clock +memory_bus_write: + process + begin + + wait until clk_i'event and clk_i = '1'; + + if reset_i = '1' then + value_s <= (others => '0'); + else + + if ce_i = '1' and bls_i /= "0000" then + + if bls_i(0) = '1' then + value_s(7 downto 0) <= data_i(7 downto 0); + end if; + if bls_i(1) = '1' then + value_s(15 downto 8) <= data_i(15 downto 8); + end if; + if bls_i(2) = '1' then + value_s(23 downto 16) <= data_i(23 downto 16); + end if; + if bls_i(3) = '1' then + value_s(31 downto 24) <= data_i(31 downto 24); + end if; + + end if; + end if; + + end process; + +end Behavioral; + diff --git a/hw/packager.c b/hw/packager.c new file mode 100644 index 0000000..65f14ac --- /dev/null +++ b/hw/packager.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +/* This util converts bitstream to more friendly format + * for select map interface with x16 line. + */ + +/* Endianness */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +int source_be = 0; +#elif __BYTE_ORDER == __BIG_ENDIAN +int source_be = 1; +#else +#error Invalid endianness. +#endif + +uint32_t swab32(uint32_t x) +{ + return x<<24 | x>>24 | + (x & (uint32_t)0x0000ff00UL)<<8 | + (x & (uint32_t)0x00ff0000UL)>>8; +} + +uint8_t fpga_swap(uint8_t x) +{ + return ((x & (uint8_t)0x01) << 7) | ((x & (uint8_t)0x02) << 5) | + ((x & (uint8_t)0x04) << 3) | ((x & (uint8_t)0x08) << 1) | + ((x & (uint8_t)0x10) >> 1) | ((x & (uint8_t)0x20) >> 3) | + ((x & (uint8_t)0x40) >> 5) | ((x & (uint8_t)0x80) >> 7); +} + +void __attribute__((noreturn)) error_usage(const char* argv0) +{ + printf("Usage: %s le|be \n", argv0); + exit(1); +} + +void __attribute__((noreturn)) error_reason(const char* argv0, const char* reason) +{ + printf("ERROR: %s\n", reason); + printf("Usage: %s le|be \n", argv0); + exit(1); +} + +int main(int argc, char** argv) +{ + int target_be, i; + uint32_t sz, szs; + uint8_t hb, lb; + char magic[4]; + FILE *fin, *fout; + + if (argc != 4) + error_usage(argv[0]); + + if (!strcmp(argv[1], "le")) + target_be = 0; + else if (!strcmp(argv[1], "be")) + target_be = 1; + else + error_usage(argv[0]); + + fin = fopen(argv[2], "r"); + if (!fin) + error_reason(argv[0], "Failed to open input file."); + + fseek(fin, 0L, SEEK_END); + sz = ftell(fin); + fseek(fin, 0L, SEEK_SET); + + if (sz & 0x01) + error_reason(argv[0], "Invalid size (not aligned)."); + + fout = fopen(argv[3], "w+"); + if (!fout) + error_reason(argv[0], "Failed to open output file."); + + /* If we're switching endianness, we need to swap the bytes */ + if (target_be != source_be) + szs = swab32(sz); + else + szs = sz; + + /* Write magic */ + magic[0] = 'F'; + magic[1] = 'P'; + magic[2] = 'G'; + magic[3] = 'A'; + fwrite(&magic, 1, 4, fout); + + /* Write size */ + fwrite(&szs, 1, sizeof(uint32_t), fout); + + /* Binary must be aligned */ + for (i = 0; i < sz / 2; i++) + { + /* Bitstream is big endian */ + fread(&hb, 1, 1, fin); + fread(&lb, 1, 1, fin); + + /* Swap the bites */ + hb = fpga_swap(hb); + lb = fpga_swap(lb); + + /* Write it */ + if (target_be) + { + fwrite(&hb, 1, 1, fout); + fwrite(&lb, 1, 1, fout); + } + else + { + fwrite(&lb, 1, 1, fout); + fwrite(&hb, 1, 1, fout); + } + } + + fclose(fout); + return 0; +} diff --git a/hw/util_pkg.vhd b/hw/util_pkg.vhd new file mode 100644 index 0000000..a9b7048 --- /dev/null +++ b/hw/util_pkg.vhd @@ -0,0 +1,51 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package util_pkg is + -- ceil(log2(n)) + function ceil_log2(n: natural) return natural; + -- ceil(a/b) + function ceil_div(a: integer; b: integer) return integer; + -- + function max(left, right: integer) return integer; + function min(left, right: integer) return integer; +end; + +--- + +package body util_pkg is + + function ceil_log2(n: natural) return natural is + begin + if n <= 1 then + return 0; + else + if n mod 2 = 0 then + return 1 + ceil_log2(n/2); + else + return 1 + ceil_log2((n+1)/2); + end if; + end if; + end function ceil_log2; + + function ceil_div(a: integer; b: integer) return integer is + begin + return (a+b-1)/b; + end function ceil_div; + + function max(left, right: integer) return integer is + begin + if left > right then return left; + else return right; + end if; + end; + + function min(left, right: integer) return integer is + begin + if left < right then return left; + else return right; + end if; + end; + +end util_pkg; diff --git a/hw/xilinx_dualport_bram.vhd b/hw/xilinx_dualport_bram.vhd new file mode 100644 index 0000000..75d928a --- /dev/null +++ b/hw/xilinx_dualport_bram.vhd @@ -0,0 +1,170 @@ +-- Xilinx dualport BRAM template, write-first mode, no delay +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.lx_dad_pkg.all; + +entity xilinx_dualport_bram is + generic + ( + -- Not all combinations are plausible for BRAMs + -- byte width: 8, 9, 32, 36 + -- we_width: 1, 2, 3, 4 + byte_width : positive := 8; + address_width : positive := 8; + we_width : positive := 4; + port_a_type : BRAM_type := READ_FIRST; + port_b_type : BRAM_type := READ_FIRST + ); + port + ( + clka : in std_logic; + rsta : in std_logic; + ena : in std_logic; + wea : in std_logic_vector((we_width-1) downto 0); + addra : in std_logic_vector((address_width-1) downto 0); + dina : in std_logic_vector(((byte_width*we_width)-1) downto 0); + douta : out std_logic_vector(((byte_width*we_width)-1) downto 0); + clkb : in std_logic; + rstb : in std_logic; + enb : in std_logic; + web : in std_logic_vector((we_width-1) downto 0); + addrb : in std_logic_vector((address_width-1) downto 0); + dinb : in std_logic_vector(((byte_width*we_width)-1) downto 0); + doutb : out std_logic_vector(((byte_width*we_width)-1) downto 0) + ); +end xilinx_dualport_bram; + +architecture Behavioral of xilinx_dualport_bram is + type ram is array (0 to ((2**address_width) - 1)) of std_logic_vector(((byte_width*we_width)-1) downto 0); + shared variable ram_block : ram := (others => (others => '0')); + +begin + +-- CLKA process +ram_process_a: + process + begin + + wait until clka'event and clka = '1'; + + if ena = '1' then + + -- Depends on the port type + case port_a_type is + when READ_FIRST => + -- Read + if rsta = '1' then + douta <= (others => '0'); + else + douta <= ram_block(to_integer(unsigned(addra))); + end if; + -- Write + for i in 0 to (we_width-1) loop + if wea(i) = '1' then + ram_block(to_integer(unsigned(addra)))(((i+1)*byte_width-1) downto (i*byte_width)) + := dina(((i+1)*byte_width-1) downto (i*byte_width)); + end if; + end loop; + + when WRITE_FIRST => + -- Write + for i in 0 to (we_width-1) loop + if wea(i) = '1' then + ram_block(to_integer(unsigned(addra)))(((i+1)*byte_width-1) downto (i*byte_width)) + := dina(((i+1)*byte_width-1) downto (i*byte_width)); + end if; + end loop; + -- Read + if rsta = '1' then + douta <= (others => '0'); + else + douta <= ram_block(to_integer(unsigned(addra))); + end if; + + when NO_CHANGE => + -- Write + for i in 0 to (we_width-1) loop + if wea(i) = '1' then + ram_block(to_integer(unsigned(addra)))(((i+1)*byte_width-1) downto (i*byte_width)) + := dina(((i+1)*byte_width-1) downto (i*byte_width)); + end if; + end loop; + -- Read (if not writing) + if to_integer(unsigned(wea)) = 0 then + if rsta = '1' then + douta <= (others => '0'); + else + douta <= ram_block(to_integer(unsigned(addra))); + end if; + end if; + end case; + + end if; + + end process; + +-- CLKB process +ram_process_b: + process + begin + + wait until clkb'event and clkb = '1'; + + if enb = '1' then + + -- Depends on the port type + case port_b_type is + when READ_FIRST => + -- Read + if rstb = '1' then + doutb <= (others => '0'); + else + doutb <= ram_block(to_integer(unsigned(addrb))); + end if; + -- Write + for i in 0 to (we_width-1) loop + if web(i) = '1' then + ram_block(to_integer(unsigned(addrb)))(((i+1)*byte_width-1) downto (i*byte_width)) + := dinb(((i+1)*byte_width-1) downto (i*byte_width)); + end if; + end loop; + + when WRITE_FIRST => + -- Write + for i in 0 to (we_width-1) loop + if web(i) = '1' then + ram_block(to_integer(unsigned(addrb)))(((i+1)*byte_width-1) downto (i*byte_width)) + := dinb(((i+1)*byte_width-1) downto (i*byte_width)); + end if; + end loop; + -- Read + if rstb = '1' then + doutb <= (others => '0'); + else + doutb <= ram_block(to_integer(unsigned(addrb))); + end if; + + when NO_CHANGE => + -- Write + for i in 0 to (we_width-1) loop + if web(i) = '1' then + ram_block(to_integer(unsigned(addrb)))(((i+1)*byte_width-1) downto (i*byte_width)) + := dinb(((i+1)*byte_width-1) downto (i*byte_width)); + end if; + end loop; + -- Read (if not writing) + if to_integer(unsigned(web)) = 0 then + if rstb = '1' then + doutb <= (others => '0'); + else + doutb <= ram_block(to_integer(unsigned(addrb))); + end if; + end if; + end case; + + end if; + + end process; + +end Behavioral; -- 2.39.2