Skip to content

Commit

Permalink
Refactor clock generation
Browse files Browse the repository at this point in the history
Move MMCM instantiation to a separate file `clk.vhd`. This improves
portability, because the MMCM is Xilinx specific. This new `clk.vhd`
file has just a single responsibility: To generate the CPU and VGA
clocks.

Also refactored the timing constraints, to make them apply more
specifically: Basically, any registers wrapped within the lines
gen_cdc : if true generate
end generate gen_cdc;
will be treated as a Clock Domain Crossing.

This was done to resolve a Xilinx warning regarding the timing
constraints and the new timing generation.

See Issue #41.
  • Loading branch information
MJoergen committed Sep 17, 2020
1 parent 5d0a346 commit b2e14c5
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 87 deletions.
15 changes: 7 additions & 8 deletions hw/xilinx/nexys4ddr/Vivado/qnice_nexys.xdc
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports CLK]
create_clock -period 10.000 -name CLK [get_ports CLK]

## Internal clock divider SLOW_CLOCK that creates the 50 MHz used throughout the system
create_generated_clock -name SLOW_CLOCK -source [get_ports CLK] -divide_by 2 [get_pins SLOW_CLOCK_reg/Q]
## Handle the Clock Domain Crossing
## Any register wrapped inside a generate statement with the name `gen_cdc`
## will be considered part of a Clock Domain Crossing.
set_false_path -from [get_clocks -of_objects [get_pins i_clk/i_mmcme2_adv/CLKOUT0]] \
-to [get_pins -hierarchical {*gen_cdc.*/D}]
set_false_path -from [get_clocks -of_objects [get_pins i_clk/i_mmcme2_adv/CLKOUT1]] \
-to [get_pins -hierarchical {*gen_cdc.*/D}]

## Make the general clocks and the pixelclock unrelated to other to avoid erroneous timing
## violations, and hopefully make everything synthesise faster
set_clock_groups -asynchronous \
-group { CLK CLKFBIN SLOW_CLOCK } \
-group [get_clocks -of_objects [get_pins clk_main/CLKOUT0]]

## EAE's combinatorial division networks take longer than
## the regular clock period, so we specify a multicycle path
## see also the comments in EAE.vhd and explanations in UG903/chapter 5/Multicycle Paths as well as ug911/page 25
Expand Down
6 changes: 6 additions & 0 deletions hw/xilinx/nexys4ddr/Vivado/qnice_nexys.xpr
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
<File Path="$PPRDIR/../../../../vhdl/hw/nexys4ddr/clk.vhd">
<FileInfo>
<Attr Name="Library" Val="xil_defaultlib"/>
<Attr Name="UsedIn" Val="synthesis"/>
</FileInfo>
</File>
<File Path="$PPRDIR/../../../../vhdl/hw/nexys4ddr/env1.vhd">
<FileInfo>
<Attr Name="Library" Val="xil_defaultlib"/>
Expand Down
100 changes: 100 additions & 0 deletions vhdl/hw/nexys4ddr/clk.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
library ieee;
use ieee.std_logic_1164.all;

library unisim;
use unisim.vcomponents.all;

entity clk is
port (
sys_clk_i : in std_logic;
clk25MHz_o : out std_logic;
clk50MHz_o : out std_logic
);
end clk;

architecture rtl of clk is

signal clkfb : std_logic;
signal clkfb_mmcm : std_logic;
signal clk25_mmcm : std_logic;
signal clk50_mmcm : std_logic;

begin

i_mmcme2_adv : MMCME2_ADV
generic map (
BANDWIDTH => "OPTIMIZED",
CLKOUT4_CASCADE => FALSE,
COMPENSATION => "ZHOLD",
STARTUP_WAIT => FALSE,
CLKIN1_PERIOD => 10.0, -- INPUT @ 100 MHz
REF_JITTER1 => 0.010,
DIVCLK_DIVIDE => 1,
CLKFBOUT_MULT_F => 8.000,
CLKFBOUT_PHASE => 0.000,
CLKFBOUT_USE_FINE_PS => FALSE,
CLKOUT0_DIVIDE_F => 31.750, -- VGA @ 25.20 MHz
CLKOUT0_PHASE => 0.000,
CLKOUT0_USE_FINE_PS => FALSE,
CLKOUT1_DIVIDE => 16, -- MAIN @ 50.00 MHz
CLKOUT1_PHASE => 0.000,
CLKOUT1_DUTY_CYCLE => 0.500,
CLKOUT1_USE_FINE_PS => FALSE
)
port map (
-- Output clocks
CLKFBOUT => clkfb_mmcm,
CLKOUT0 => clk25_mmcm,
CLKOUT1 => clk50_mmcm,
-- Input clock control
CLKFBIN => clkfb,
CLKIN1 => sys_clk_i,
CLKIN2 => '0',
-- Tied to always select the primary input clock
CLKINSEL => '1',
-- Ports for dynamic reconfiguration
DADDR => (others => '0'),
DCLK => '0',
DEN => '0',
DI => (others => '0'),
DO => open,
DRDY => open,
DWE => '0',
-- Ports for dynamic phase shift
PSCLK => '0',
PSEN => '0',
PSINCDEC => '0',
PSDONE => open,
-- Other control and status signals
LOCKED => open,
CLKINSTOPPED => open,
CLKFBSTOPPED => open,
PWRDWN => '0',
RST => '0'
);


-------------------------------------
-- Output buffering
-------------------------------------

clkfb_bufg : BUFG
port map (
I => clkfb_mmcm,
O => clkfb
);

clk25_bufg : BUFG
port map (
I => clk25_mmcm,
O => clk25MHz_o
);

clk50_bufg : BUFG
port map (
I => clk50_mmcm,
O => clk50MHz_o
);

end architecture rtl;

44 changes: 7 additions & 37 deletions vhdl/hw/nexys4ddr/env1.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -160,27 +160,13 @@ begin
eae_data_out or
sd_data_out;

-- Non portable (Xilinx specific) way to generate the 25.175 MHz pixel clock
-- Comment out and replace by the below-mentioned process "generate_clk25MHz"
-- if you want to be portable and have a look at hw/README.md "General advise for porting"
clk_main: mmcme2_base
generic map
(
clkin1_period => 10.0, -- 100 MHz (10 ns)
clkfbout_mult_f => 8.0, -- 800 MHz common multiply
divclk_divide => 1, -- 800 MHz /1 common divide to stay within 600MHz-1600MHz range
clkout0_divide_f => 31.7775571 -- 25.175 MHz / 31.7775571 == pixelclock
)
port map
(
pwrdwn => '0',
rst => '0',
clkin1 => CLK,
clkfbin => clk_fb_main,
clkfbout => clk_fb_main,
clkout0 => clk25MHz, -- pixelclock
locked => pll_locked_main
);
i_clk : entity work.clk
port map
(
sys_clk_i => CLK,
clk25MHz_o => clk25MHz,
clk50MHz_o => SLOW_CLOCK
);

-- QNICE CPU
cpu : entity work.QNICE_CPU
Expand Down Expand Up @@ -457,22 +443,6 @@ begin
end if;
end process;

-- clock divider: create a 50 MHz clock from the 100 MHz input
generate_slow_clock : process(CLK)
begin
if rising_edge(CLK) then
SLOW_CLOCK <= not SLOW_CLOCK;
end if;
end process;

-- clock divider of the clock divider: create a 25 MHz clock from the 50 MHz clock
-- generate_clk25MHz : process(SLOW_CLOCK)
-- begin
-- if rising_edge(SLOW_CLOCK) then
-- clk25MHz <= not clk25MHz;
-- end if;
-- end process;

-- debug mode handling: if switch 2 is on then:
-- show the current cpu address in realtime on the LEDs
-- on halt show the PC of the HALT command (aka address bus value) on TIL
Expand Down
21 changes: 15 additions & 6 deletions vhdl/vga/true_dual_port_ram.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,21 @@ begin
-- Port B
----------

p_b : process (b_clk_i)
begin
if rising_edge(b_clk_i) then
b_rd_data_o <= mem(conv_integer(b_rd_addr_i));
end if;
end process p_b;
-- Use a generate statement to make it easier to reference
-- from the constraint file.
-- Since the Palette RAM gets implemented as Distributed RAM instead
-- of Block RAM, there will be an explicit output register, which
-- needs to be covered by the timing constraint.
gen_cdc : if true generate

p_b : process (b_clk_i)
begin
if rising_edge(b_clk_i) then
b_rd_data_o <= mem(conv_integer(b_rd_addr_i));
end if;
end process p_b;

end generate gen_cdc;

end architecture synthesis;

78 changes: 42 additions & 36 deletions vhdl/vga_multicolor.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -241,42 +241,48 @@ begin
-- Clock Domain Crossing
-----------------------------------------------

p_cpu_to_vga : process (vga_clk_i)
begin
if rising_edge(vga_clk_i) then
meta_output_enable <= cpu_output_enable;
meta_display_offset <= cpu_display_offset;
meta_font_offset <= cpu_font_offset;
meta_palette_offset <= cpu_palette_offset;
meta_cursor_enable <= cpu_cursor_enable;
meta_cursor_blink <= cpu_cursor_blink;
meta_cursor_size <= cpu_cursor_size;
meta_cursor_x <= cpu_cursor_x;
meta_cursor_y <= cpu_cursor_y;
meta_adjust_x <= cpu_adjust_x;
meta_adjust_y <= cpu_adjust_y;

vga_output_enable <= meta_output_enable;
vga_display_offset <= meta_display_offset;
vga_font_offset <= meta_font_offset;
vga_palette_offset <= meta_palette_offset;
vga_cursor_enable <= meta_cursor_enable;
vga_cursor_blink <= meta_cursor_blink;
vga_cursor_size <= meta_cursor_size;
vga_cursor_x <= meta_cursor_x;
vga_cursor_y <= meta_cursor_y;
vga_adjust_x <= meta_adjust_x;
vga_adjust_y <= meta_adjust_y;
end if;
end process p_cpu_to_vga;

p_vga_to_cpu : process (cpu_clk_i)
begin
if rising_edge(cpu_clk_i) then
meta_pixel_y <= vga_pixel_y;
cpu_pixel_y <= meta_pixel_y;
end if;
end process p_vga_to_cpu;
-- Use a generate statement to make it easier to reference
-- from the constraint file.
gen_cdc : if true generate

p_cpu_to_vga : process (vga_clk_i)
begin
if rising_edge(vga_clk_i) then
meta_output_enable <= cpu_output_enable;
meta_display_offset <= cpu_display_offset;
meta_font_offset <= cpu_font_offset;
meta_palette_offset <= cpu_palette_offset;
meta_cursor_enable <= cpu_cursor_enable;
meta_cursor_blink <= cpu_cursor_blink;
meta_cursor_size <= cpu_cursor_size;
meta_cursor_x <= cpu_cursor_x;
meta_cursor_y <= cpu_cursor_y;
meta_adjust_x <= cpu_adjust_x;
meta_adjust_y <= cpu_adjust_y;

vga_output_enable <= meta_output_enable;
vga_display_offset <= meta_display_offset;
vga_font_offset <= meta_font_offset;
vga_palette_offset <= meta_palette_offset;
vga_cursor_enable <= meta_cursor_enable;
vga_cursor_blink <= meta_cursor_blink;
vga_cursor_size <= meta_cursor_size;
vga_cursor_x <= meta_cursor_x;
vga_cursor_y <= meta_cursor_y;
vga_adjust_x <= meta_adjust_x;
vga_adjust_y <= meta_adjust_y;
end if;
end process p_cpu_to_vga;

p_vga_to_cpu : process (cpu_clk_i)
begin
if rising_edge(cpu_clk_i) then
meta_pixel_y <= vga_pixel_y;
cpu_pixel_y <= meta_pixel_y;
end if;
end process p_vga_to_cpu;

end generate gen_cdc;


-----------------------------------------------
Expand Down

0 comments on commit b2e14c5

Please sign in to comment.