├── .gitignore ├── high_counter.v ├── pll_adv_example ├── Makefile └── pll_adv_example.srcs │ └── sources_1 │ └── new │ ├── pll_adv_example_tb.v │ └── pll_adv_example.v ├── pll_example ├── Makefile ├── pll_example.srcs │ └── sources_1 │ │ └── new │ │ ├── pll_led_tb.v │ │ ├── divider_synth.v │ │ └── pll_led.v └── Basys3_Master.xdc ├── LICENSE ├── duty_cycle_check.v ├── period_check.v ├── period_count.v ├── .github └── workflows │ └── avocado.yml ├── tb ├── test_period_check.py ├── test_dyn_reconf.py ├── test_phase_shift.py ├── test_high_counter.py ├── test_period_count.py ├── mktb.sh ├── high_counter_tb.v ├── test_base.py ├── phase_shift_check_tb.v ├── period_check_tb.v ├── test_chained_pll.py ├── duty_cycle_check_tb.v ├── period_count_tb.v ├── chained_pll_tb.v ├── test_freq_gen.py ├── freq_gen_tb.v ├── Makefile ├── phase_shift_tb.v ├── test_mmcme2_base.py ├── test_plle2_base.py ├── plle2_base_tb.v ├── test_plle2_adv.py ├── mmcme2_base_tb.v ├── dyn_reconf_tb.v └── plle2_adv_tb.v ├── phase_shift_check.v ├── freq_gen.v ├── phase_shift.v ├── plle2_base.v ├── mmcme2_base.v ├── plle2_adv.v ├── dyn_reconf.v └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.vcd 2 | *_tb 3 | tb/__pycache__/ 4 | 5 | pll_adv_example/pll_adv_example.cache/ 6 | pll_adv_example/pll_adv_example.hw/ 7 | pll_adv_example/pll_adv_example.ip_user_files/ 8 | pll_adv_example/pll_adv_example.runs/ 9 | pll_adv_example/pll_adv_example.sim/ 10 | 11 | 12 | vivado.jou 13 | vivado.log 14 | vivado_pid19952.str 15 | pll_adv_example/pll_adv_example.xpr -------------------------------------------------------------------------------- /high_counter.v: -------------------------------------------------------------------------------- 1 | /* 2 | * high_counter.v: Counts all highs in the input signal since the last release of the rst signal. 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | module high_counter ( 11 | input clk, 12 | input rst, 13 | 14 | output reg [31:0] count); 15 | 16 | always @(posedge rst or posedge clk) begin 17 | if (rst) begin 18 | count <= 0; 19 | end else begin 20 | count <= count + 1; 21 | end 22 | end 23 | endmodule 24 | 25 | -------------------------------------------------------------------------------- /pll_adv_example/Makefile: -------------------------------------------------------------------------------- 1 | pll_adv_example_build: pll_adv_example.srcs/sources_1/new/pll_adv_example_tb.v pll_adv_example.srcs/sources_1/new/pll_adv_example.v ../plle2_adv.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v 2 | - iverilog pll_adv_example.srcs/sources_1/new/pll_adv_example_tb.v pll_adv_example.srcs/sources_1/new/pll_adv_example.v ../plle2_adv.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v -o pll_adv_example_tb 3 | pll_adv_example_test: pll_adv_example_build 4 | - vvp pll_adv_example_tb 5 | -------------------------------------------------------------------------------- /pll_example/Makefile: -------------------------------------------------------------------------------- 1 | pll_led_build: pll_example.srcs/sources_1/new/pll_led_tb.v pll_example.srcs/sources_1/new/pll_led.v pll_example.srcs/sources_1/new/divider_synth.v ../plle2_base.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v 2 | - iverilog pll_example.srcs/sources_1/new/pll_led_tb.v -DFF_NUM=2 pll_example.srcs/sources_1/new/pll_led.v pll_example.srcs/sources_1/new/divider_synth.v ../plle2_base.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v -o pll_led_tb 3 | pll_led_test: pll_led_build 4 | - vvp pll_led_tb 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License (ISC) 2 | Copyright 2019 Till Mahlburg 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 7 | 8 | -------------------------------------------------------------------------------- /pll_example/pll_example.srcs/sources_1/new/pll_led_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * pll_led_tb.v: Test bench for pll_led.v 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `ifndef FF_NUM 11 | `define FF_NUM 25 12 | `endif 13 | 14 | `timescale 1 ns / 1 ps 15 | 16 | module pll_led_tb (); 17 | reg rst; 18 | reg clk; 19 | wire [6:0] led; 20 | 21 | pll_led #( 22 | .FF_NUM(`FF_NUM)) 23 | dut( 24 | .clk(clk), 25 | .RST(rst), 26 | .led(led)); 27 | 28 | initial begin 29 | $dumpfile("pll_led_tb.vcd"); 30 | $dumpvars(0, pll_led_tb); 31 | 32 | clk = 0; 33 | rst = 0; 34 | 35 | 36 | #10; 37 | rst = 1; 38 | #10; 39 | rst = 0; 40 | #100000; 41 | $finish; 42 | end 43 | 44 | /* 100 MHz Clock */ 45 | always #(10 / 2) clk <= ~clk; 46 | endmodule 47 | -------------------------------------------------------------------------------- /pll_adv_example/pll_adv_example.srcs/sources_1/new/pll_adv_example_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * pll_adv_example_tb.v: Test bench for pll_adv_example.v 3 | * author: Till Mahlburg 4 | * year: 2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 10000 14 | `endif 15 | 16 | module pll_adv_example_tb (); 17 | reg RST; 18 | reg clk; 19 | wire [7:0] led; 20 | 21 | pll_adv_example dut( 22 | .clk(clk), 23 | .RST(RST), 24 | .led(led)); 25 | 26 | initial begin 27 | $dumpfile("pll_adv_example_tb.vcd"); 28 | $dumpvars(0, pll_adv_example_tb); 29 | 30 | RST = 0; 31 | clk = 0; 32 | 33 | #10; 34 | RST = 1; 35 | #10; 36 | 37 | /* TEST CASES */ 38 | RST = 0; 39 | #`WAIT_INTERVAL 40 | 41 | $finish; 42 | end 43 | 44 | always #5 clk <= ~clk; 45 | endmodule 46 | -------------------------------------------------------------------------------- /duty_cycle_check.v: -------------------------------------------------------------------------------- 1 | /* 2 | * duty_cycle_check.v: This module determines the duty cycle of the input 3 | * signal and compares it against the desired duty cycle. 4 | * author: Till Mahlburg 5 | * year: 2020 6 | * organization: Universität Leipzig 7 | * license: ISC 8 | * 9 | */ 10 | 11 | `timescale 1 ns / 1 ps 12 | 13 | module duty_cycle_check ( 14 | /* duty cycle multiplied by 1000 (500 for 50 % in high) */ 15 | input [31:0] desired_duty_cycle_1000, 16 | input [31:0] clk_period_1000, 17 | input clk, 18 | input reset, 19 | input LOCKED, 20 | 21 | /* 0 - clk duty cycle matches desired_duty_cycle 22 | * 1 - clk duty cycle does not match the desired_duty_cycle 23 | */ 24 | output reg fail); 25 | 26 | always @(posedge clk or posedge reset) begin 27 | if (reset) begin 28 | fail <= 0; 29 | end else if (LOCKED) begin 30 | #(((clk_period_1000 / 1000.0) * (desired_duty_cycle_1000 / 1000.0)) - 0.1); 31 | if (clk != 1) begin 32 | fail <= 1; 33 | end 34 | #0.2; 35 | if (clk != 0) begin 36 | fail <= 1; 37 | end 38 | end else begin 39 | fail <= 0; 40 | end 41 | end 42 | endmodule 43 | -------------------------------------------------------------------------------- /period_check.v: -------------------------------------------------------------------------------- 1 | /* 2 | * period_check.v: Determines if the given input has a stable value. 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | module period_check ( 13 | input RST, 14 | input PWRDWN, 15 | input clk, 16 | input [31:0] period_length, 17 | output reg period_stable); 18 | 19 | /* tracks the last period length measured */ 20 | integer period_length_last; 21 | 22 | /* checks if the measured period length didn't change since the last rising edge of the clk */ 23 | always @(posedge clk or posedge RST or posedge PWRDWN) begin 24 | if (PWRDWN) begin 25 | period_stable <= 1'bx; 26 | period_length_last <= 0; 27 | end else if (RST) begin 28 | period_stable <= 1'b0; 29 | period_length_last <= 0; 30 | end else if (period_length == period_length_last && period_length != 0) begin 31 | period_stable <= 1'b1; 32 | period_length_last <= period_length; 33 | end else begin 34 | period_stable <= 1'b0; 35 | period_length_last <= period_length; 36 | end 37 | end 38 | 39 | endmodule 40 | -------------------------------------------------------------------------------- /period_count.v: -------------------------------------------------------------------------------- 1 | /* 2 | * period_count.v: Measures the length of the period of the input signal. 3 | * author: Till Mahlburg 4 | * year: 2019-2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | module period_count #( 13 | /* set the precision of the period count */ 14 | parameter RESOLUTION = 0.01) ( 15 | input RST, 16 | input PWRDWN, 17 | input clk, 18 | output reg [31:0] period_length_1000); 19 | 20 | integer period_counter; 21 | 22 | /* count up continuously with the given resolution */ 23 | always begin 24 | #0.001 25 | if (RST) begin 26 | period_counter <= 0; 27 | end else begin 28 | period_counter <= period_counter + 1; 29 | end 30 | #(RESOLUTION - 0.001); 31 | end 32 | 33 | /* output counted value and reset counter on every rising clk edge */ 34 | always @(posedge clk or posedge RST or posedge PWRDWN) begin 35 | if (PWRDWN || RST) begin 36 | period_length_1000 <= 0; 37 | end else begin 38 | period_length_1000 <= (period_counter * RESOLUTION * 1000); 39 | period_counter <= 0; 40 | end 41 | end 42 | 43 | endmodule 44 | -------------------------------------------------------------------------------- /pll_example/pll_example.srcs/sources_1/new/divider_synth.v: -------------------------------------------------------------------------------- 1 | /* 2 | * divider.v: Simple clock divider based on D-flipflops. 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | module divider_synth #( 13 | parameter FF_NUM = 27) ( 14 | input clk_in, 15 | input RST, 16 | 17 | output clk_out); 18 | 19 | wire [(FF_NUM - 1):0] D_in; 20 | wire [(FF_NUM - 1):0] clk_div; 21 | 22 | dff dff_0 ( 23 | .clk(clk_in), 24 | .RST(RST), 25 | .D(D_in[0]), 26 | .Q(clk_div[0])); 27 | 28 | genvar i; 29 | generate 30 | for (i = 1; i < FF_NUM; i = i + 1) 31 | begin : dff_gen_label 32 | dff dff_inst ( 33 | .clk(clk_div[i-1]), 34 | .RST(RST), 35 | .D(D_in[i]), 36 | .Q(clk_div[i])); 37 | end 38 | endgenerate 39 | 40 | assign D_in = ~clk_div; 41 | assign clk_out = clk_div[(FF_NUM - 1)]; 42 | 43 | endmodule 44 | 45 | 46 | module dff ( 47 | input D, 48 | input clk, 49 | input RST, 50 | output reg Q); 51 | 52 | always @ (posedge clk or posedge RST) begin 53 | if (RST == 1) begin 54 | Q <= 1'b0; 55 | end else begin 56 | Q <= D; 57 | end 58 | end 59 | endmodule 60 | -------------------------------------------------------------------------------- /.github/workflows/avocado.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "test" 16 | test: 17 | # The type of runner that the job will run on 18 | runs-on: ubuntu-latest 19 | 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 23 | - uses: actions/checkout@v2 24 | # Use Python 25 | - uses: actions/setup-python@v1 26 | 27 | # Install dependencies 28 | - run: | 29 | sudo apt-get install iverilog 30 | sudo pip install avocado-framework 31 | 32 | # Run Avocado 33 | - run: | 34 | cd $GITHUB_WORKSPACE/tb 35 | avocado run test_dyn_reconf.py test_freq_gen.py test_high_counter.py test_mmcme2_base.py test_period_check.py test_period_count.py test_phase_shift.py test_plle2_base.py test_plle2_adv.py 36 | -------------------------------------------------------------------------------- /tb/test_period_check.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class PeriodCheckTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the period_check.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_period_check_test(self): 25 | """test period check""" 26 | 27 | test_files = ["period_check_tb.v"] 28 | src_files = ["period_check.v"] 29 | # copy source files so we know that all required files are there 30 | self.copy_sources(test_files, src_files) 31 | 32 | verilog_files = test_files + src_files 33 | 34 | sim_res = self.simulate(verilog_files, "") 35 | sim_output = sim_res.stdout_text 36 | 37 | # save vcd for analysis 38 | shutil.copy("period_check_tb.vcd", self.outputdir) 39 | 40 | self.check_test_bench_output(sim_output, 7) 41 | 42 | def test_period_check(self): 43 | """ 44 | :avocado: tags: quick,verilog 45 | """ 46 | 47 | self.generic_period_check_test() 48 | -------------------------------------------------------------------------------- /phase_shift_check.v: -------------------------------------------------------------------------------- 1 | /* 2 | * phase_shift_check.v: Helper module to check the phase shift of an input 3 | * signal (clk_shifted) relative to another signal (clk) and compare it 4 | * with the desired shift. 5 | * author: Till Mahlburg 6 | * year: 2020 7 | * organization: Universität Leipzig 8 | * license: ISC 9 | * 10 | */ 11 | 12 | `timescale 1 ns / 1 ps 13 | 14 | /* checks the phase shift of the input signal in relation to clk */ 15 | module phase_shift_check ( 16 | input [31:0] desired_shift_1000, 17 | input [31:0] clk_period_1000, 18 | input clk_shifted, 19 | input clk, 20 | input rst, 21 | /* input to tell the module, that the inputs are ready */ 22 | input LOCKED, 23 | output reg fail); 24 | 25 | always @(posedge clk or posedge rst) begin 26 | if (rst) begin 27 | fail <= 0; 28 | end else if (LOCKED) begin 29 | if (desired_shift_1000 > 0) begin 30 | #(((desired_shift_1000 / 1000.0) * ((clk_period_1000 / 1000.0) / 360.0)) - 0.1); 31 | if (clk_shifted !== 0) begin 32 | fail <= 1; 33 | end 34 | end else if (desired_shift_1000 < 0) begin 35 | #(((clk_period_1000 / 1000.0) + ((desired_shift_1000 / 1000.0) * ((clk_period_1000 / 1000.0) / 360.0))) - 0.1); 36 | if (clk_shifted !== 0) begin 37 | fail <= 1; 38 | end 39 | end 40 | #0.2; 41 | if (clk_shifted !== 1 && desired_shift_1000 !== 0) begin 42 | fail <= 1; 43 | end 44 | end 45 | end 46 | endmodule 47 | -------------------------------------------------------------------------------- /tb/test_dyn_reconf.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class DynReconfTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the dyn_reconf.v module 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is neeeded in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process finds the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_dyn_reconf_test(self, wait_interval=1000, clk_period=10): 25 | """ 26 | test dyn reconf 27 | """ 28 | 29 | test_files = ["dyn_reconf_tb.v"] 30 | src_files = ["dyn_reconf.v"] 31 | # copy source files, so all the required files are there 32 | self.copy_sources(test_files, src_files) 33 | 34 | verilog_files = test_files + src_files 35 | 36 | sim_res = self.simulate(verilog_files, 37 | "-DWAIT_INTERVAL={} -DCLK_PERIOD={}". 38 | format(wait_interval, clk_period)) 39 | sim_output = sim_res.stdout_text 40 | 41 | # save vcd for analysis 42 | shutil.copy("dyn_reconf_tb.vcd", self.outputdir) 43 | 44 | self.check_test_bench_output(sim_output, 29) 45 | 46 | def test_dyn_reconf_1000_10(self): 47 | """ 48 | :avocado: tags: quick,verilog 49 | """ 50 | 51 | self.generic_dyn_reconf_test(wait_interval=1000, clk_period=10) 52 | -------------------------------------------------------------------------------- /tb/test_phase_shift.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class PhaseShiftTest(Test, test_base.Mixin): 10 | """ 11 | This class test the phase_shift.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_phase_shift_test(self, wait_interval=100): 25 | """test phase shift""" 26 | 27 | test_files = ["phase_shift_tb.v"] 28 | src_files = ["phase_shift.v"] 29 | # copy source files so we know that all required files are there 30 | self.copy_sources(test_files, src_files) 31 | 32 | verilog_files = test_files + src_files 33 | 34 | sim_res = self.simulate( 35 | verilog_files, "-DWAIT_INTERVAL={}".format(wait_interval)) 36 | sim_output = sim_res.stdout_text 37 | 38 | # save vcd for analysis 39 | shutil.copy("phase_shift_tb.vcd", self.outputdir) 40 | 41 | self.check_test_bench_output(sim_output, 18) 42 | 43 | def test_phase_shift_100(self): 44 | """ 45 | :avocado: tags: quick,verilog 46 | """ 47 | 48 | self.generic_phase_shift_test(wait_interval=100) 49 | 50 | def test_phase_shift_1000(self): 51 | """ 52 | :avocado: tags: quick,verilog 53 | """ 54 | 55 | self.generic_phase_shift_test(wait_interval=1000) 56 | -------------------------------------------------------------------------------- /tb/test_high_counter.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class HighCounterTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the high_counter.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_high_counter_test(self, wait_interval=1000): 25 | """test high counter""" 26 | 27 | test_files = ["high_counter_tb.v"] 28 | src_files = ["high_counter.v"] 29 | # copy source files so we know that all required files are there 30 | self.copy_sources(test_files, src_files) 31 | 32 | verilog_files = test_files + src_files 33 | 34 | sim_res = self.simulate(verilog_files, 35 | "-DWAIT_INTERVAL={}".format(wait_interval)) 36 | sim_output = sim_res.stdout_text 37 | 38 | # save vcd for analysis 39 | shutil.copy("high_counter_tb.vcd", self.outputdir) 40 | 41 | self.check_test_bench_output(sim_output, 4) 42 | 43 | def test_high_counter_1000(self): 44 | """ 45 | :avocado: tags: quick,verilog 46 | """ 47 | 48 | self.generic_high_counter_test(wait_interval=1000) 49 | 50 | def test_high_counter_10000(self): 51 | """ 52 | :avocado: tags: quick, verilog 53 | """ 54 | self.generic_high_counter_test(wait_interval=10000) 55 | -------------------------------------------------------------------------------- /tb/test_period_count.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class PeriodCountTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the period_count.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_period_count_test(self, wait_interval=100, resolution=1.0): 25 | """ 26 | test period count 27 | """ 28 | 29 | test_files = ["period_count_tb.v"] 30 | src_files = ["period_count.v"] 31 | # copy source files so we know that all required files are there 32 | self.copy_sources(test_files, src_files) 33 | 34 | verilog_files = test_files + src_files 35 | 36 | sim_res = self.simulate(verilog_files, 37 | "-DWAIT_INTERVAL={} -DRESOLUTION={}". 38 | format(wait_interval, resolution)) 39 | sim_output = sim_res.stdout_text 40 | 41 | # save vcd for analysis 42 | shutil.copy("period_count_tb.vcd", self.outputdir) 43 | 44 | self.check_test_bench_output(sim_output, 6) 45 | 46 | def test_period_count_100_1_0(self): 47 | """ 48 | :avocado: tags: quick,verilog 49 | """ 50 | 51 | self.generic_period_count_test(wait_interval=100, resolution=1.0) 52 | 53 | def test_period_count_100_0_1(self): 54 | """ 55 | :avocado: tags: quick,verilog 56 | """ 57 | 58 | self.generic_period_count_test(wait_interval=100, resolution=0.1) 59 | -------------------------------------------------------------------------------- /pll_example/pll_example.srcs/sources_1/new/pll_led.v: -------------------------------------------------------------------------------- 1 | /* 2 | * pll_led.v: Example program working in simulation as well as on real hardware. Tested on Digilent Basys 3. 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | module pll_led #( 13 | parameter FF_NUM = 25) ( 14 | input clk, 15 | input RST, 16 | 17 | output [6:0] led); 18 | 19 | /* divide the output signal, so one can see the results at the LEDs */ 20 | wire [3:0] pll_out; 21 | 22 | genvar i; 23 | 24 | generate 25 | for (i = 0; i < 4; i = i + 1) begin : generate_div 26 | divider_synth #( 27 | .FF_NUM(FF_NUM)) div ( 28 | .clk_in(pll_out[i]), 29 | .RST(RST), 30 | .clk_out(led[i+1])); 31 | end 32 | endgenerate 33 | 34 | 35 | 36 | wire CLKFB; 37 | /* More information about the instantiiation can be found in Xilinx UG953 509ff. */ 38 | PLLE2_BASE #( 39 | .CLKFBOUT_MULT(8), 40 | .CLKFBOUT_PHASE(90.0), 41 | .CLKIN1_PERIOD(10.0), 42 | 43 | .CLKOUT0_DIVIDE(128), 44 | .CLKOUT1_DIVIDE(64), 45 | .CLKOUT2_DIVIDE(32), 46 | .CLKOUT3_DIVIDE(16), 47 | .CLKOUT4_DIVIDE(128), 48 | .CLKOUT5_DIVIDE(128), 49 | 50 | .CLKOUT0_DUTY_CYCLE(0.5), 51 | .CLKOUT1_DUTY_CYCLE(0.5), 52 | .CLKOUT2_DUTY_CYCLE(0.5), 53 | .CLKOUT3_DUTY_CYCLE(0.5), 54 | .CLKOUT4_DUTY_CYCLE(0.9), 55 | .CLKOUT5_DUTY_CYCLE(0.1), 56 | 57 | .CLKOUT0_PHASE(0.0), 58 | .CLKOUT1_PHASE(0.0), 59 | .CLKOUT2_PHASE(0.0), 60 | .CLKOUT3_PHASE(0.0), 61 | .CLKOUT4_PHASE(0.0), 62 | .CLKOUT5_PHASE(0.0), 63 | 64 | .DIVCLK_DIVIDE(1)) 65 | pll ( 66 | .CLKOUT0(pll_out[0]), 67 | .CLKOUT1(pll_out[1]), 68 | .CLKOUT2(pll_out[2]), 69 | .CLKOUT3(pll_out[3]), 70 | .CLKOUT4(led[5]), 71 | .CLKOUT5(led[6]), 72 | 73 | .CLKFBOUT(CLKFB), 74 | .LOCKED(led[0]), 75 | .CLKIN1(clk), 76 | 77 | .PWRDWN(0), 78 | .RST(RST), 79 | 80 | .CLKFBIN(CLKFB)); 81 | endmodule 82 | -------------------------------------------------------------------------------- /tb/mktb.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | ##################################################### 4 | # Name: mktb.sh 5 | # 6 | # Creates the skeleton of a Verilog testbench 7 | # 8 | # Usage: mktb.sh 9 | # 10 | # Date of Creation: 2019-08-30 11 | ###################################################### 12 | 13 | # Functions 14 | 15 | usage () { 16 | cat <<- EOF 17 | usage: $(basename $0) [-h] 18 | 19 | Creates the skeleton of a Verilog testbench. 20 | 21 | OPTIONS: 22 | -h shows this help 23 | EOF 24 | } 25 | 26 | tb () { 27 | cat << EOF 28 | /* 29 | * $1_tb.v: Test bench for $1.v 30 | * author: Till Mahlburg 31 | * year: $(date +%Y) 32 | * organization: Universität Leipzig 33 | * license: ISC 34 | * 35 | */ 36 | 37 | \`timescale 1 ns / 1 ps 38 | 39 | \`ifndef WAIT_INTERVAL 40 | \`define WAIT_INTERVAL 1000 41 | \`endif 42 | 43 | module $1_tb (); 44 | reg rst; 45 | 46 | integer pass_count; 47 | integer fail_count; 48 | 49 | /* adjust according to the number of test cases */ 50 | localparam total = ; 51 | 52 | $1 dut( 53 | ); 54 | 55 | initial begin 56 | \$dumpfile("$1_tb.vcd"); 57 | \$dumpvars(0, $1_tb); 58 | 59 | rst = 0; 60 | 61 | pass_count = 0; 62 | fail_count = 0; 63 | 64 | #10; 65 | rst = 1; 66 | #10; 67 | 68 | /* TEST CASES */ 69 | 70 | if ((pass_count + fail_count) == total) begin 71 | \$display("PASSED: number of test cases"); 72 | pass_count = pass_count + 1; 73 | end else begin 74 | \$display("FAILED: number of test cases"); 75 | fail_count = fail_count + 1; 76 | end 77 | 78 | \$display("%0d/%0d PASSED", pass_count, (total + 1)); 79 | \$finish; 80 | end 81 | endmodule 82 | EOF 83 | } 84 | 85 | # Main functionality. Evaluates the given arguments. 86 | 87 | main () { 88 | while getopts "h" opt; do 89 | case $opt in 90 | h ) usage ;; 91 | \? ) usage 92 | exit 1 ;; 93 | esac 94 | done 95 | shift $((OPTIND - 1)) 96 | 97 | tb "$@" 98 | 99 | } 100 | main "$@" 101 | -------------------------------------------------------------------------------- /tb/high_counter_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * high_counter_tb.v: Testbench for high_counter.v 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | `ifndef WAIT_INTERVAL 12 | `define WAIT_INTERVAL 1000 13 | `endif 14 | 15 | module high_counter_tb (); 16 | reg clk; 17 | reg rst; 18 | 19 | wire [31:0] count; 20 | 21 | integer pass_count; 22 | integer fail_count; 23 | /* change according to the number of test cases */ 24 | localparam total = 3; 25 | 26 | integer clk_period; 27 | 28 | high_counter dut ( 29 | .clk(clk), 30 | .rst(rst), 31 | .count(count)); 32 | 33 | initial begin 34 | $dumpfile("high_counter_tb.vcd"); 35 | $dumpvars(0, high_counter_tb); 36 | 37 | rst = 0; 38 | clk = 0; 39 | clk_period = 10; 40 | 41 | pass_count = 0; 42 | fail_count = 0; 43 | 44 | #10; 45 | rst = 1; 46 | #10; 47 | if (count === 0) begin 48 | $display("PASSED: rst"); 49 | pass_count = pass_count + 1; 50 | end else begin 51 | $display("FAILED: rst"); 52 | fail_count = fail_count + 1; 53 | end 54 | 55 | rst = 0; 56 | #`WAIT_INTERVAL 57 | if (count == (`WAIT_INTERVAL / clk_period)) begin 58 | $display("PASSED: correct count"); 59 | pass_count = pass_count + 1; 60 | end else begin 61 | $display("FAILED: correct count"); 62 | fail_count = fail_count + 1; 63 | end 64 | rst = 1; 65 | #10; 66 | clk_period = 100; 67 | clk = 0; 68 | rst = 0; 69 | #`WAIT_INTERVAL 70 | if (count == (`WAIT_INTERVAL / clk_period)) begin 71 | $display("PASSED: correct count after changing clk"); 72 | pass_count = pass_count + 1; 73 | end else begin 74 | $display("FAILED: correct count after changing clk"); 75 | fail_count = fail_count + 1; 76 | end 77 | 78 | if (pass_count + fail_count == total) begin 79 | $display("PASSED: correct number of test cases"); 80 | pass_count = pass_count + 1; 81 | end else begin 82 | $display("FAILED: correct number of test cases"); 83 | fail_count = fail_count + 1; 84 | end 85 | 86 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 87 | $finish; 88 | end 89 | 90 | always #(clk_period / 2) clk = ~clk; 91 | endmodule 92 | -------------------------------------------------------------------------------- /freq_gen.v: -------------------------------------------------------------------------------- 1 | /* 2 | * freq_gen.v: Generates the frequency depending on the given period length while allowing manipulation using 3 | * the inputs provided. Starts frequency generation on the first rising 4 | * edge of the input clk after the period_stable input is 1. 5 | * author: Till Mahlburg 6 | * year: 2019-2020 7 | * organization: Universität Leipzig 8 | * license: ISC 9 | * 10 | */ 11 | 12 | `timescale 1 ns / 1 ps 13 | 14 | module freq_gen ( 15 | /* global multiplier in the PLL, multiplied by 1000 */ 16 | input [31:0] M_1000, 17 | /* global divisor in the PLL */ 18 | input [31:0] D, 19 | /* output specific divisor in the PLL, multiplied by 1000 */ 20 | input [31:0] O_1000, 21 | 22 | input RST, 23 | input PWRDWN, 24 | /* informs the module if the given period length (ref_period) can be trusted */ 25 | input period_stable, 26 | input [31:0] ref_period_1000, 27 | /* needed to achieve phase lock, by detecting the first rising edge of the clk 28 | * and aligning the output clk to it */ 29 | input clk, 30 | output reg out, 31 | /* period length is multiplied by 1000 for higher precision */ 32 | output reg [31:0] out_period_length_1000); 33 | 34 | /* tracks when to start the frequency generation */ 35 | reg start; 36 | 37 | /* generate the wanted frequency */ 38 | always begin 39 | if (PWRDWN) begin 40 | out <= 1'bx; 41 | start <= 1'bx; 42 | #1; 43 | end else if (RST) begin 44 | out <= 1'b0; 45 | start <= 1'b0; 46 | #1; 47 | end else if (ref_period_1000 > 0 && start) begin 48 | /* The formula used is based on Equation 3-2 on page 72 of Xilinx UG472, 49 | * but adjusted to calculate the period length not the frequency. 50 | * Multiplying by 1.0 forces verilog to calculate with floating 51 | * point number. Multiplying the out_period_length_1000 by 1000 is an 52 | * easy solution to returning floating point numbers. 53 | */ 54 | out_period_length_1000 <= ((ref_period_1000 / 1000.0) * ((D * (O_1000 / 1000.0) * 1.0) / (M_1000 / 1000.0)) * 1000); 55 | out <= ~out; 56 | #(((ref_period_1000 / 1000.0) * ((D * (O_1000 / 1000.0) * 1.0) / (M_1000 / 1000.0))) / 2.0); 57 | end else begin 58 | out <= 1'b0; 59 | #1; 60 | end 61 | end 62 | 63 | /* detect the first rising edge of the input clk, after period_stable is achieved */ 64 | always @(posedge clk) begin 65 | if (period_stable && !start) begin 66 | #((ref_period_1000 / 1000.0) - 1); 67 | start <= 1'b1; 68 | end else if (!period_stable) begin 69 | start <= 1'b0; 70 | end 71 | end 72 | 73 | endmodule 74 | -------------------------------------------------------------------------------- /tb/test_base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import shutil 4 | import re 5 | import os 6 | 7 | from avocado.utils import process 8 | 9 | 10 | class Mixin(): 11 | """ 12 | This class implements the common function of the tests for the PLL 13 | simulation and helper modules. 14 | """ 15 | 16 | def copy_to_workdir(self, file_list): 17 | """Copy files to the working directory of the test""" 18 | 19 | for f in file_list: 20 | shutil.copy(f, self.workdir) 21 | 22 | def copy_sources(self, test_sources, project_sources): 23 | """Copy source files from test and and project to work directory. 24 | 25 | test_sources: sources from test directory 26 | project_sources: sources from project directory 27 | """ 28 | self.copy_to_workdir([os.path.join(self.basedir, s) 29 | for s in test_sources]) 30 | self.copy_to_workdir([os.path.join(self.project_dir, s) 31 | for s in project_sources]) 32 | 33 | def simulate(self, verilog_sources, compiler_option=""): 34 | """Simulate Verilog sources with iverilog 35 | 36 | name for the out file is generated from the name of the first 37 | Verilog source 38 | 39 | returns CommandResult of simulation 40 | """ 41 | 42 | simulation_filename = "{}.out".format(verilog_sources[0]) 43 | cmd_res = process.run("iverilog -Wall -o {} {} {}".format( 44 | simulation_filename, 45 | " ".join(verilog_sources), compiler_option), 46 | allow_output_check="both") 47 | 48 | cmd_res = process.run("vvp {}".format( 49 | simulation_filename), allow_output_check="both") 50 | return cmd_res 51 | 52 | def check_test_bench_output(self, output, result_number): 53 | """Check output of test bench for failed cases 54 | 55 | result_number: number of expected cases 56 | """ 57 | 58 | count = 0 59 | for line in output.split("\n"): 60 | res = re.match( 61 | r'(?PPASSED|FAILED):\s+(?P.*)', line) 62 | if res is not None: 63 | count += 1 64 | if res.group("result") != "PASSED": 65 | self.fail("Failed: {}".format(res.group("description"))) 66 | 67 | if result_number > 0: 68 | # avoid false positive test result when the test bench 69 | # ends with error 70 | self.assertEqual(result_number, count, 71 | "Expected {} test results, but found {}.".format( 72 | result_number, count)) 73 | -------------------------------------------------------------------------------- /phase_shift.v: -------------------------------------------------------------------------------- 1 | /* 2 | * phase_shift.v: Shifts the input clock by the given degree and can change 3 | it's duty cycle. 4 | * author: Till Mahlburg 5 | * year: 2019-2020 6 | * organization: Universität Leipzig 7 | * license: ISC 8 | * 9 | */ 10 | 11 | `timescale 1 ns / 1 ps 12 | 13 | module phase_shift ( 14 | input RST, 15 | input PWRDWN, 16 | input clk, 17 | /* shift in degrees */ 18 | input signed [31:0] shift_1000, 19 | /* period length of the clock */ 20 | input [31:0] clk_period_1000, 21 | /* duty cycle in percent */ 22 | input [6:0] duty_cycle, 23 | 24 | output reg lock, 25 | output reg clk_shifted); 26 | 27 | /* The formulas used here are composed of the following: 28 | * - clk_period_1000 / 1000.0 calculates the actual period length, which 29 | * has been multiplied by 1000 to enable a higher precision. 30 | * - shift * ((clk_period_1000 / 1000.0) / 360.0) calculate the basic shift 31 | * as a multiple of a 360th of the period of the clk to shift 32 | * - if the desired shift is negative, we just add a shift by 33 | * 360 degrees and add the (negative) shift: 34 | * clk_period + (shift * (clk_period / 360.0)) 35 | * - to calculate the duty cycle, we delay or rush the output of 36 | * 0 by adding (or subtracting) the desired duty cycle in 100th 37 | * of the input clk period from the default (50): 38 | * (duty_cycle + 50.0) * (clk_period / 100.0) 39 | */ 40 | 41 | /* calculate when to put out high */ 42 | always @(posedge clk or posedge RST or posedge PWRDWN) begin 43 | if (!RST && !PWRDWN) begin 44 | if (shift_1000 < 0) begin 45 | clk_shifted <= #((clk_period_1000 / 1000.0) + ((shift_1000 / 1000.0) * ((clk_period_1000 / 1000.0) / 360.0))) 1; 46 | end else begin 47 | clk_shifted <= #((shift_1000 / 1000.0) * ((clk_period_1000 / 1000.0) / 360.0)) 1; 48 | end 49 | end 50 | end 51 | 52 | /* calculate when to put out low and return when the phase and duty cycle is correctly set */ 53 | always @(negedge clk or posedge RST) begin 54 | if (RST) begin 55 | clk_shifted <= 1'b0; 56 | lock <= 1'b0; 57 | end else if (lock !== 1'bx) begin 58 | if (shift_1000 < 0) begin 59 | clk_shifted <= #((clk_period_1000 / 1000.0) + ((shift_1000/ 1000.0) * ((clk_period_1000 / 1000.0) / 360.0)) + ((duty_cycle + 50.0) * ((clk_period_1000 / 1000.0) / 100.0))) 0; 60 | lock <= #((clk_period_1000 / 1000.0) + ((shift_1000/ 1000.0) * ((clk_period_1000 / 1000.0) / 360.0)) + ((duty_cycle + 50.0) * ((clk_period_1000 / 1000.0) / 100.0))) 1'b1; 61 | end else begin 62 | clk_shifted <= #((shift_1000/ 1000.0) * ((clk_period_1000 / 1000.0) / 360.0) + ((duty_cycle + 50.0) * ((clk_period_1000 / 1000.0) / 100.0))) 0; 63 | lock <= #((shift_1000/ 1000.0) * ((clk_period_1000 / 1000.0) / 360.0) + ((duty_cycle + 50.0) * ((clk_period_1000 / 1000.0) / 100.0))) 1'b1; 64 | end 65 | end 66 | end 67 | 68 | /* PWRDWN for good */ 69 | always begin 70 | if (PWRDWN) begin 71 | clk_shifted <= 1'bx; 72 | lock <= 1'bx; 73 | #0.001; 74 | end else begin 75 | #1; 76 | end; 77 | end 78 | 79 | endmodule 80 | -------------------------------------------------------------------------------- /tb/phase_shift_check_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * phase_shift_check_tb.v: Test bench for phase_shift_check.v 3 | * author: Till Mahlburg 4 | * year: 2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 1000 14 | `endif 15 | 16 | `ifndef DESIRED_SHIFT 17 | `define DESIRED_SHIFT 45 18 | `endif 19 | 20 | `ifndef CLK_PERIOD 21 | `define CLK_PERIOD 20 22 | `endif 23 | 24 | module phase_shift_check_tb (); 25 | reg rst; 26 | reg clk; 27 | wire clk_shifted; 28 | reg LOCKED; 29 | 30 | wire fail; 31 | 32 | integer shift; 33 | 34 | integer pass_count; 35 | integer fail_count; 36 | 37 | /* adjust according to the number of test cases */ 38 | localparam total = 4; 39 | 40 | phase_shift_check dut ( 41 | .desired_shift_1000(`DESIRED_SHIFT * 1000), 42 | .clk_period_1000(`CLK_PERIOD * 1000), 43 | .clk_shifted(clk_shifted), 44 | .clk(clk), 45 | .rst(rst), 46 | .LOCKED(LOCKED), 47 | .fail(fail)); 48 | 49 | phase_shift ps ( 50 | .PWRDWN(1'b0), 51 | .RST(rst), 52 | .clk(clk), 53 | .shift(shift), 54 | .clk_period_1000(`CLK_PERIOD * 1000), 55 | .duty_cycle(50), 56 | .clk_shifted(clk_shifted)); 57 | 58 | initial begin 59 | $dumpfile("phase_shift_check_tb.vcd"); 60 | $dumpvars(0, phase_shift_check_tb); 61 | 62 | rst = 0; 63 | 64 | shift = `DESIRED_SHIFT; 65 | clk = 0; 66 | LOCKED = 0; 67 | 68 | pass_count = 0; 69 | fail_count = 0; 70 | 71 | #10; 72 | rst = 1; 73 | #10; 74 | 75 | if (fail == 1'b0) begin 76 | $display("PASSED: rst"); 77 | pass_count = pass_count + 1; 78 | end else begin 79 | $display("FAILED: rst"); 80 | fail_count = fail_count + 1; 81 | end 82 | 83 | rst = 0; 84 | #(`CLK_PERIOD * 2); 85 | 86 | if (fail == 1'b0) begin 87 | $display("PASSED: LOCKED"); 88 | pass_count = pass_count + 1; 89 | end else begin 90 | $display("FAILED: LOCKED"); 91 | fail_count = fail_count + 1; 92 | end 93 | 94 | LOCKED = 1; 95 | #`WAIT_INTERVAL; 96 | if (fail == 1'b0) begin 97 | $display("PASSED: shifts match"); 98 | pass_count = pass_count + 1; 99 | end else begin 100 | $display("FAILED: shifts match"); 101 | fail_count = fail_count + 1; 102 | end 103 | 104 | shift = shift + 45; 105 | #`WAIT_INTERVAL; 106 | if (fail == 1'b1) begin 107 | $display("PASSED: shifts don't match"); 108 | pass_count = pass_count + 1; 109 | end else begin 110 | $display("FAILED: shifts don't match"); 111 | fail_count = fail_count + 1; 112 | end 113 | 114 | 115 | if ((pass_count + fail_count) == total) begin 116 | $display("PASSED: number of test cases"); 117 | pass_count = pass_count + 1; 118 | end else begin 119 | $display("FAILED: number of test cases"); 120 | fail_count = fail_count + 1; 121 | end 122 | 123 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 124 | $finish; 125 | end 126 | 127 | always #(`CLK_PERIOD / 2.0) clk <= ~clk; 128 | endmodule 129 | -------------------------------------------------------------------------------- /tb/period_check_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * period_check_tb.v: Test bench for period_check.v 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | module period_check_tb (); 13 | reg RST; 14 | reg PWRDWN; 15 | reg clk; 16 | reg [31:0] period_length; 17 | 18 | wire period_stable; 19 | 20 | integer pass_count; 21 | integer fail_count; 22 | 23 | /* adjust according to the number of test cases */ 24 | localparam total = 6; 25 | 26 | period_check dut( 27 | .RST(RST), 28 | .PWRDWN(PWRDWN), 29 | .clk(clk), 30 | .period_length(period_length), 31 | .period_stable(period_stable)); 32 | 33 | initial begin 34 | $dumpfile("period_check_tb.vcd"); 35 | $dumpvars(0, period_check_tb); 36 | 37 | RST = 0; 38 | PWRDWN = 0; 39 | clk = 0; 40 | period_length = 20; 41 | 42 | pass_count = 0; 43 | fail_count = 0; 44 | 45 | #10; 46 | RST = 1; 47 | #10; 48 | 49 | if (period_stable === 1'b0) begin 50 | $display("PASSED: RST"); 51 | pass_count = pass_count + 1; 52 | end else begin 53 | $display("FAILED: RST"); 54 | fail_count = fail_count + 1; 55 | end 56 | 57 | RST = 0; 58 | #(period_length * 2) 59 | if (period_stable === 1'b1) begin 60 | $display("PASSED: period stable"); 61 | pass_count = pass_count + 1; 62 | end else begin 63 | $display("FAILED: period stable"); 64 | fail_count = fail_count + 1; 65 | end 66 | 67 | period_length = 10; 68 | #((period_length / 2.0) + 1); 69 | if (period_stable === 1'b0) begin 70 | $display("PASSED: period unstable"); 71 | pass_count = pass_count + 1; 72 | end else begin 73 | $display("FAILED: period unstable"); 74 | fail_count = fail_count + 1; 75 | end 76 | 77 | #(period_length - 1); 78 | if (period_stable === 1'b0) begin 79 | $display("PASSED: period still unstable"); 80 | pass_count = pass_count + 1; 81 | end else begin 82 | $display("FAILED: period still unstable"); 83 | fail_count = fail_count + 1; 84 | end 85 | 86 | #1; 87 | if (period_stable === 1'b1) begin 88 | $display("PASSED: period stable again"); 89 | pass_count = pass_count + 1; 90 | end else begin 91 | $display("FAILED: period stable again"); 92 | fail_count = fail_count + 1; 93 | end 94 | 95 | PWRDWN = 1; 96 | #10; 97 | if (period_stable === 1'bx) begin 98 | $display("PASSED: PWRDWN"); 99 | pass_count = pass_count + 1; 100 | end else begin 101 | $display("FAILED: PWRDWN"); 102 | fail_count = fail_count + 1; 103 | end 104 | 105 | 106 | if ((pass_count + fail_count) == total) begin 107 | $display("PASSED: number of test cases"); 108 | pass_count = pass_count + 1; 109 | end else begin 110 | $display("FAILED: number of test cases"); 111 | fail_count = fail_count + 1; 112 | end 113 | 114 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 115 | $finish; 116 | end 117 | 118 | always #(period_length / 2.0) clk <= ~clk; 119 | endmodule 120 | -------------------------------------------------------------------------------- /tb/test_chained_pll.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class ChainedPllTest(Test, test_base.Mixin): 10 | """ 11 | This class tests, if chained PLLs work as expected. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_chained_pll_test(self, 25 | wait_interval=1000, 26 | clkin1_period=5, 27 | clkfbout_mult=5, 28 | divclk_divide=1, 29 | pll_num=3): 30 | """ 31 | test chained plls 32 | """ 33 | 34 | test_files = ["chained_pll_tb.v"] 35 | src_files=["plle2_base.v", 36 | "dyn_reconf.v", 37 | "freq_gen.v", 38 | "period_check.v", 39 | "period_count.v", 40 | "phase_shift.v", 41 | "pll.v"] 42 | # copy source files 43 | self.copy_sources(test_files, src_files) 44 | 45 | verilog_files = test_files + src_files 46 | 47 | sim_res = self.simulate(verilog_files, 48 | "-DWAIT_INTERVAL={} \ 49 | -DCLKIN1_PERIOD={} \ 50 | -DCLKFBOUT_MULT={} \ 51 | -DDIVCLK_DIVIDE={} \ 52 | -DPLL_NUM={}". 53 | format(wait_interval, 54 | clkin1_period, 55 | clkfbout_mult, 56 | divclk_divide, 57 | pll_num)) 58 | 59 | sim_output = sim_res.stdout_text 60 | 61 | shutil.copy("chained_pll_tb.vcd", self.outputdir) 62 | 63 | self.check_test_bench_output(sim_output, pll_num + 8) 64 | 65 | def test_chained_pll_1000_5_5_1_3(self): 66 | """ 67 | :avocado: tags: quick,verilog 68 | """ 69 | 70 | self.generic_chained_pll_test(wait_interval=1000, 71 | clkin1_period=5, 72 | clkfbout_mult=5, 73 | divclk_divide=1, 74 | pll_num=3) 75 | 76 | def test_chained_pll_1000_20_50_2_2(self): 77 | """ 78 | :avocado: tags: quick,verilog 79 | """ 80 | 81 | self.generic_chained_pll_test(wait_interval=1000, 82 | clkin1_period=20, 83 | clkfbout_mult=50, 84 | divclk_divide=2, 85 | pll_num=2) 86 | -------------------------------------------------------------------------------- /tb/duty_cycle_check_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * duty_cycle_check_tb.v: Test bench for duty_cycle_check.v 3 | * author: Till Mahlburg 4 | * year: 2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 1000 14 | `endif 15 | 16 | `ifndef DESIRED_DUTY_CYCLE 17 | `define DESIRED_DUTY_CYCLE 0.5 18 | `endif 19 | 20 | `ifndef CLK_PERIOD 21 | `define CLK_PERIOD 10 22 | `endif 23 | 24 | module duty_cycle_check_tb (); 25 | reg rst; 26 | reg clk; 27 | reg LOCKED; 28 | 29 | wire fail; 30 | 31 | reg [31:0] duty_cycle_1000; 32 | 33 | integer pass_count; 34 | integer fail_count; 35 | 36 | /* adjust according to the number of test cases */ 37 | localparam total = 4; 38 | 39 | duty_cycle_check dut ( 40 | .desired_duty_cycle_1000(`DESIRED_DUTY_CYCLE * 1000), 41 | .clk_period_1000(`CLK_PERIOD * 1000), 42 | .clk(clk), 43 | .reset(rst), 44 | .LOCKED(LOCKED), 45 | .fail(fail)); 46 | 47 | initial begin 48 | $dumpfile("duty_cycle_check_tb.vcd"); 49 | $dumpvars(0, duty_cycle_check_tb); 50 | 51 | duty_cycle_1000 = 1000 * `DESIRED_DUTY_CYCLE; 52 | 53 | rst = 0; 54 | clk = 0; 55 | LOCKED = 0; 56 | 57 | pass_count = 0; 58 | fail_count = 0; 59 | 60 | #1; 61 | clk = 1; 62 | #10; 63 | rst = 1; 64 | #10; 65 | 66 | if (fail == 1'b0) begin 67 | $display("PASSED: reset"); 68 | pass_count = pass_count + 1; 69 | end else begin 70 | $display("FAILED: reset"); 71 | fail_count = fail_count + 1; 72 | end 73 | 74 | rst = 0; 75 | #(`CLK_PERIOD * 3); 76 | 77 | if (fail == 1'b0) begin 78 | $display("PASSED: LOCKED"); 79 | pass_count = pass_count + 1; 80 | end else begin 81 | $display("FAILED: LOCKED"); 82 | fail_count = fail_count + 1; 83 | end 84 | 85 | LOCKED = 1; 86 | #`WAIT_INTERVAL; 87 | 88 | if (fail == 1'b0) begin 89 | $display("PASSED: duty cycle matches"); 90 | pass_count = pass_count + 1; 91 | end else begin 92 | $display("FAILED: duty cycle matches"); 93 | fail_count = fail_count + 1; 94 | end 95 | 96 | duty_cycle_1000 = duty_cycle_1000 - 100; 97 | #`WAIT_INTERVAL; 98 | 99 | if (fail == 1'b1) begin 100 | $display("PASSED: duty cycle differs"); 101 | pass_count = pass_count + 1; 102 | end else begin 103 | $display("FAILED: duty cycle differs"); 104 | fail_count = fail_count + 1; 105 | end 106 | 107 | 108 | if ((pass_count + fail_count) == total) begin 109 | $display("PASSED: number of test cases"); 110 | pass_count = pass_count + 1; 111 | end else begin 112 | $display("FAILED: number of test cases"); 113 | fail_count = fail_count + 1; 114 | end 115 | 116 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 117 | $finish; 118 | end 119 | 120 | always @(posedge clk) begin 121 | #(`CLK_PERIOD * (duty_cycle_1000 / 1000.0)) clk <= ~clk; 122 | end 123 | always @(negedge clk) begin 124 | #(`CLK_PERIOD * (1 - (duty_cycle_1000 / 1000.0))) clk <= ~clk; 125 | end 126 | endmodule 127 | -------------------------------------------------------------------------------- /tb/period_count_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * period_count_tb.v: Test bench for period_count.v 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 100 14 | `endif 15 | 16 | `ifndef RESOLUTION 17 | `define RESOLUTION 0.1 18 | `endif 19 | 20 | module period_count_tb (); 21 | reg RST; 22 | reg PWRDWN; 23 | reg clk; 24 | 25 | wire [31:0] period_length_1000; 26 | 27 | reg [31:0] clk_period; 28 | 29 | integer pass_count; 30 | integer fail_count; 31 | 32 | /* adjust according to the number of test cases */ 33 | localparam total = 5; 34 | 35 | period_count #( 36 | .RESOLUTION(`RESOLUTION)) 37 | dut( 38 | .RST(RST), 39 | .PWRDWN(PWRDWN), 40 | .clk(clk), 41 | .period_length_1000(period_length_1000)); 42 | 43 | initial begin 44 | $dumpfile("period_count_tb.vcd"); 45 | $dumpvars(0, period_count_tb); 46 | 47 | RST = 0; 48 | PWRDWN = 0; 49 | clk = 0; 50 | clk_period = 10; 51 | 52 | pass_count = 0; 53 | fail_count = 0; 54 | 55 | #10; 56 | RST = 1; 57 | #10; 58 | 59 | if (period_length_1000 === 0) begin 60 | $display("PASSED: RST"); 61 | pass_count = pass_count + 1; 62 | end else begin 63 | $display("FAILED: RST"); 64 | fail_count = fail_count + 1; 65 | end 66 | 67 | RST = 0; 68 | #`WAIT_INTERVAL; 69 | 70 | if ((period_length_1000 / 1000.0) == clk_period) begin 71 | $display("PASSED: period = 10"); 72 | pass_count = pass_count + 1; 73 | end else begin 74 | $display("FAILED: period = 10"); 75 | fail_count = fail_count + 1; 76 | end 77 | 78 | clk_period = 13; 79 | #`WAIT_INTERVAL; 80 | 81 | if ((period_length_1000 / 1000.0) == clk_period) begin 82 | $display("PASSED: period = 13"); 83 | pass_count = pass_count + 1; 84 | end else begin 85 | $display("FAILED: period = 13"); 86 | fail_count = fail_count + 1; 87 | end 88 | 89 | clk_period = 1; 90 | #`WAIT_INTERVAL; 91 | 92 | if ((period_length_1000 / 1000.0) == clk_period) begin 93 | $display("PASSED: period = 1"); 94 | pass_count = pass_count + 1; 95 | end else begin 96 | $display("FAILED: period = 1"); 97 | fail_count = fail_count + 1; 98 | end 99 | 100 | clk_period = (`WAIT_INTERVAL / 2); 101 | #`WAIT_INTERVAL; 102 | 103 | if ((period_length_1000 / 1000.0) == `WAIT_INTERVAL / 2) begin 104 | $display("PASSED: period = %0d", (`WAIT_INTERVAL / 2)); 105 | pass_count = pass_count + 1; 106 | end else begin 107 | $display("FAILED: period = %0d", (`WAIT_INTERVAL / 2)); 108 | fail_count = fail_count + 1; 109 | end 110 | 111 | if ((pass_count + fail_count) == total) begin 112 | $display("PASSED: number of test cases"); 113 | pass_count = pass_count + 1; 114 | end else begin 115 | $display("FAILED: number of test cases"); 116 | fail_count = fail_count + 1; 117 | end 118 | 119 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 120 | $finish; 121 | end 122 | 123 | always #(clk_period / 2.0) clk <= ~clk; 124 | 125 | endmodule 126 | -------------------------------------------------------------------------------- /pll_adv_example/pll_adv_example.srcs/sources_1/new/pll_adv_example.v: -------------------------------------------------------------------------------- 1 | /* 2 | * pll_adv_example.v: Example program working in simulation as well as it should on real hardware. 3 | * To be tested on Digilent Basys 3. 4 | * author: Till Mahlburg 5 | * year: 2019 6 | * organization: Universität Leipzig 7 | * license: ISC 8 | * 9 | */ 10 | 11 | `timescale 1 ns / 1 ps 12 | 13 | module pll_adv_example ( 14 | input clk, 15 | input RST, 16 | output [7:0] led); 17 | 18 | reg [6:0] DADDR; 19 | reg [15:0] DI; 20 | wire [15:0] DO; 21 | reg DEN; 22 | reg DWE; 23 | wire DRDY; 24 | 25 | wire CLKFB; 26 | /* More information about the instantiiation can be found in Xilinx UG953 509ff. */ 27 | PLLE2_ADV #( 28 | .CLKFBOUT_MULT(8), 29 | .CLKFBOUT_PHASE(90.0), 30 | .CLKIN1_PERIOD(10.0), 31 | .CLKIN2_PERIOD(10.0), 32 | 33 | .CLKOUT0_DIVIDE(128), 34 | .CLKOUT1_DIVIDE(2), 35 | .CLKOUT2_DIVIDE(32), 36 | .CLKOUT3_DIVIDE(16), 37 | .CLKOUT4_DIVIDE(128), 38 | .CLKOUT5_DIVIDE(128), 39 | 40 | .CLKOUT0_DUTY_CYCLE(0.5), 41 | .CLKOUT1_DUTY_CYCLE(0.5), 42 | .CLKOUT2_DUTY_CYCLE(0.5), 43 | .CLKOUT3_DUTY_CYCLE(0.5), 44 | .CLKOUT4_DUTY_CYCLE(0.9), 45 | .CLKOUT5_DUTY_CYCLE(0.1), 46 | 47 | .CLKOUT0_PHASE(0.0), 48 | .CLKOUT1_PHASE(0.0), 49 | .CLKOUT2_PHASE(0.0), 50 | .CLKOUT3_PHASE(0.0), 51 | .CLKOUT4_PHASE(0.0), 52 | .CLKOUT5_PHASE(0.0), 53 | 54 | .DIVCLK_DIVIDE(1)) 55 | pll ( 56 | .CLKOUT0(led[0]), 57 | .CLKOUT1(led[1]), 58 | .CLKOUT2(led[2]), 59 | .CLKOUT3(led[3]), 60 | .CLKOUT4(led[5]), 61 | .CLKOUT5(led[6]), 62 | 63 | .CLKFBOUT(CLKFB), 64 | .LOCKED(led[7]), 65 | .CLKIN1(clk), 66 | .CLKIN2(clk), 67 | .CLKINSEL(1'b1), 68 | 69 | .DADDR(DADDR), 70 | .DI(DI), 71 | .DO(DO), 72 | .DWE(DWE), 73 | .DEN(DEN), 74 | .DRDY(DRDY), 75 | .DCLK(led[0]), 76 | 77 | .PWRDWN(0), 78 | .RST(RST), 79 | 80 | .CLKFBIN(CLKFB)); 81 | 82 | integer step = 0; 83 | /* CLKOUT1 will be dynamically reconfigured */ 84 | always @(posedge clk) begin 85 | /* After some time to achieve LOCK & DRDY status, we can write the first value into ClkReg1 86 | * for CLKOUT1 */ 87 | if (led[7] && DRDY && step == 0) begin 88 | /* Address of ClkReg1 for CLKOUT1 */ 89 | DADDR <= 7'h0A; 90 | /* PHASE MUX = 3 91 | * RESERVED = 0 92 | * HIGH TIME = 16 93 | * LOW TIME = 32 */ 94 | /* This translates to a CLKOUT1_DIVDE of 48, a CLKOUT1_DUTY_CYCLE of 0.666 95 | * and a phase offset of 3x45° relative to the VCO */ 96 | DI = 16'b011_0_010000_100000; 97 | DEN <= 1; 98 | DWE <= 1; 99 | step <= 1; 100 | /* Next, we will disable DEN and DWE, as soon as DRDY and LOCK are achieved again */ 101 | end else if (led[7] && step == 1) begin 102 | DEN <= 0; 103 | DWE <= 0; 104 | step <= 2; 105 | /* Now we will write into ClkReg2 for CLKOUT1 */ 106 | end else if (led[7] && DRDY && step == 2) begin 107 | DEN <= 1; 108 | DWE <= 1; 109 | /* Address of ClkReg2 of CLKOUT1 */ 110 | DADDR = 7'h0B; 111 | /* RESERVED = 000000 112 | * MX = 2b'00 113 | * EDGE = 0 114 | * NO COUNT = 0 115 | * DELAY TIME = 3 */ 116 | /* This will add an additional phase delay as high as 3 VCO clock cycles */ 117 | DI <= 16'b000000_00_0_0_000011; 118 | step <= 3; 119 | end else if (led[7] && step == 3) begin 120 | /* Disable Read/Write again */ 121 | DEN <= 0; 122 | DWE <= 0; 123 | step <= 4; 124 | end 125 | end 126 | endmodule 127 | -------------------------------------------------------------------------------- /tb/chained_pll_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * chained_pll_tb.v: Test bench for chained plls. 3 | * author: Till Mahlburg 4 | * year: 2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 1000 14 | `endif 15 | 16 | `ifndef CLKIN1_PERIOD 17 | `define CLKIN1_PERIOD 5 18 | `endif 19 | 20 | `ifndef CLKFBOUT_MULT 21 | `define CLKFBOUT_MULT 5 22 | `endif 23 | 24 | `ifndef DIVCLK_DIVIDE 25 | `define DIVCLK_DIVIDE 1 26 | `endif 27 | 28 | /* number of PLLs which are chained after one another */ 29 | `ifndef PLL_NUM 30 | `define PLL_NUM 3 31 | `endif 32 | 33 | module chained_pll_tb (); 34 | reg rst; 35 | reg clk; 36 | 37 | integer pass_count; 38 | integer fail_count; 39 | 40 | /* adjust according to the number of test cases */ 41 | localparam total = (`PLL_NUM + 7); 42 | 43 | wire [(`PLL_NUM - 1):0] CLKOUT[5:0]; 44 | wire [(`PLL_NUM):0] CLKFBOUT; 45 | wire [(`PLL_NUM - 1):0] LOCKED; 46 | assign CLKFBOUT[0] = CLKFBOUT[`PLL_NUM]; 47 | 48 | wire [31:0] period_1000[0:5]; 49 | wire [31:0] period_1000_fb; 50 | 51 | genvar i; 52 | generate 53 | for (i = 0; i < `PLL_NUM; i = i + 1) begin : pll 54 | PLLE2_BASE #( 55 | .CLKFBOUT_MULT(`CLKFBOUT_MULT), 56 | .CLKFBOUT_PHASE(0.0), 57 | .CLKIN1_PERIOD(`CLKIN1_PERIOD), 58 | .DIVCLK_DIVIDE(`DIVCLK_DIVIDE)) pll ( 59 | .CLKOUT0(CLKOUT[0][i]), 60 | .CLKOUT1(CLKOUT[1][i]), 61 | .CLKOUT2(CLKOUT[2][i]), 62 | .CLKOUT3(CLKOUT[3][i]), 63 | .CLKOUT4(CLKOUT[4][i]), 64 | .CLKOUT5(CLKOUT[5][i]), 65 | .CLKFBOUT(CLKFBOUT[i+1]), 66 | .CLKFBIN(CLKFBOUT[i]), 67 | .LOCKED(LOCKED[i]), 68 | .RST(rst), 69 | .CLKIN1(clk), 70 | .PWRDWN(1'b0)); 71 | end 72 | 73 | for (i = 0; i <= 5; i = i + 1) begin : period_count 74 | period_count period_count ( 75 | .RST(rst), 76 | .clk(CLKOUT[i][`PLL_NUM - 1]), 77 | .period_length_1000(period_1000[i])); 78 | end 79 | endgenerate 80 | 81 | period_count period_count_fb ( 82 | .RST(rst), 83 | .clk(CLKFBOUT[`PLL_NUM - 1]), 84 | .period_length_1000(period_1000_fb)); 85 | 86 | integer k; 87 | initial begin 88 | $dumpfile("chained_pll_tb.vcd"); 89 | $dumpvars(0, chained_pll_tb); 90 | 91 | rst = 0; 92 | clk = 0; 93 | 94 | pass_count = 0; 95 | fail_count = 0; 96 | 97 | #10; 98 | rst = 1; 99 | #10; 100 | /* TEST CASES */ 101 | for (k = 0; k < `PLL_NUM; k = k + 1) begin 102 | if ((CLKOUT[0][k] & CLKOUT[1][k] & CLKOUT[2][k] & CLKOUT[3][k] & CLKOUT[4][k] & CLKOUT[5][k] & CLKFBOUT[k+1] & LOCKED[k]) == 0) begin 103 | $display("PASSED: RST on PLL %0d", k); 104 | pass_count = pass_count + 1; 105 | end else begin 106 | $display("FAILED: RST on PLL %0d", k); 107 | fail_count = fail_count + 1; 108 | end 109 | end 110 | 111 | rst = 0; 112 | #`WAIT_INTERVAL; 113 | 114 | for (k = 0; k <= 5; k = k + 1) begin 115 | if ((period_1000[k] / 1000.0) == (`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * 1.0) / `CLKFBOUT_MULT))) begin 116 | $display("PASSED: CLKOUT%0d frequency", k); 117 | pass_count = pass_count + 1; 118 | end else begin 119 | $display("FAILED: CLKOUT%0d frequency", k); 120 | fail_count = fail_count + 1; 121 | end 122 | end 123 | 124 | if ((period_1000_fb / 1000.0) == (`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * 1.0) / `CLKFBOUT_MULT))) begin 125 | $display("PASSED: CLKFBOUT frequency"); 126 | pass_count = pass_count + 1; 127 | end else begin 128 | $display("FAILED: CLKFBOUT frequency"); 129 | fail_count = fail_count + 1; 130 | end 131 | 132 | if ((pass_count + fail_count) == total) begin 133 | $display("PASSED: number of test cases"); 134 | pass_count = pass_count + 1; 135 | end else begin 136 | $display("FAILED: number of test cases"); 137 | fail_count = fail_count + 1; 138 | end 139 | 140 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 141 | $finish; 142 | end 143 | 144 | always #(`CLKIN1_PERIOD / 2.0) clk <= ~clk; 145 | endmodule 146 | -------------------------------------------------------------------------------- /tb/test_freq_gen.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class FreqGenTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the freq_gen.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_freq_gen_test(self, 25 | wait_interval=1000, 26 | m_1000=1000, 27 | d=1, 28 | o_1000=1000): 29 | """test freq gen""" 30 | 31 | test_files = ["freq_gen_tb.v"] 32 | src_files = ["freq_gen.v", "high_counter.v"] 33 | # copy source files so we know that all required files are there 34 | self.copy_sources(test_files, src_files) 35 | 36 | verilog_files = test_files + src_files 37 | 38 | sim_res = self.simulate(verilog_files, 39 | "-DWAIT_INTERVAL={} -DM_1000={} -DD={} \ 40 | -DO_1000={}". 41 | format(wait_interval, m_1000, d, o_1000)) 42 | sim_output = sim_res.stdout_text 43 | 44 | # save vcd for analysis 45 | shutil.copy("freq_gen_tb.vcd", self.outputdir) 46 | 47 | self.check_test_bench_output(sim_output, 7) 48 | 49 | def test_freq_gen_1000_1_1_1(self): 50 | """ 51 | :avocado: tags: quick,verilog 52 | """ 53 | 54 | self.generic_freq_gen_test(wait_interval=1000, 55 | m_1000=1000, 56 | d=1, 57 | o_1000=1000) 58 | 59 | def test_freq_gen_1200_2_3_4(self): 60 | """ 61 | :avocado: tags: quick,verilog 62 | """ 63 | 64 | self.generic_freq_gen_test(wait_interval=1200, 65 | m_1000=2000, 66 | d=3, 67 | o_1000=4000) 68 | 69 | def test_freq_gen_1000_3_5_1(self): 70 | """ 71 | :avocado: tags: quick,verilog 72 | """ 73 | 74 | self.generic_freq_gen_test(wait_interval=1000, 75 | m_1000=3000, 76 | d=5, 77 | o_1000=1000) 78 | 79 | def test_freq_gen_1000_8_1_4(self): 80 | """ 81 | :avocado: tags: quick,verilog 82 | """ 83 | 84 | self.generic_freq_gen_test(wait_interval=1000, 85 | m_1000=8000, 86 | d=1, 87 | o_1000=4000) 88 | 89 | def test_freq_gen_1000_8_1_3_3(self): 90 | """ 91 | :avocado: tags: quick,verilog 92 | """ 93 | 94 | self.generic_freq_gen_test(wait_interval=1000, 95 | m_1000=8000, 96 | d=1, 97 | o_1000=3300) 98 | 99 | def test_freq_gen_1000_8_1_6_15(self): 100 | """ 101 | :avocado: tags: quick,verilog 102 | """ 103 | 104 | self.generic_freq_gen_test(wait_interval=1000, 105 | m_1000=8000, 106 | d=1, 107 | o_1000=6150) 108 | 109 | def test_freq_gen_1000_8_8__1__2_1(self): 110 | """ 111 | :avocado: tags: quick,verilog 112 | """ 113 | 114 | self.generic_freq_gen_test(wait_interval=1000, 115 | m_1000=8800, 116 | d=1, 117 | 118 | o_1000=2100) 119 | -------------------------------------------------------------------------------- /plle2_base.v: -------------------------------------------------------------------------------- 1 | /* 2 | * plle2_base.v: Simulates the PLLE2_BASE pll of the xilinx 7 series. This 3 | * is just a wrapper around the actual logic found in pll.v 4 | * author: Till Mahlburg 5 | * year: 2019-2020 6 | * organization: Universität Leipzig 7 | * license: ISC 8 | * 9 | */ 10 | 11 | `timescale 1 ns / 1 ps 12 | 13 | /* A reference for the interface can be found in Xilinx UG953 page 509ff */ 14 | module PLLE2_BASE #( 15 | /* not implemented */ 16 | parameter BANDWIDTH = "OPTIMIZED", 17 | 18 | parameter CLKFBOUT_MULT = 5, 19 | parameter CLKFBOUT_PHASE = 0.0, 20 | 21 | /* is ignored, but should be set */ 22 | parameter CLKIN1_PERIOD = 0.0, 23 | 24 | parameter CLKOUT0_DIVIDE = 1, 25 | parameter CLKOUT1_DIVIDE = 1, 26 | parameter CLKOUT2_DIVIDE = 1, 27 | parameter CLKOUT3_DIVIDE = 1, 28 | parameter CLKOUT4_DIVIDE = 1, 29 | parameter CLKOUT5_DIVIDE = 1, 30 | 31 | parameter CLKOUT0_DUTY_CYCLE = 0.5, 32 | parameter CLKOUT1_DUTY_CYCLE = 0.5, 33 | parameter CLKOUT2_DUTY_CYCLE = 0.5, 34 | parameter CLKOUT3_DUTY_CYCLE = 0.5, 35 | parameter CLKOUT4_DUTY_CYCLE = 0.5, 36 | parameter CLKOUT5_DUTY_CYCLE = 0.5, 37 | 38 | parameter CLKOUT0_PHASE = 0.0, 39 | parameter CLKOUT1_PHASE = 0.0, 40 | parameter CLKOUT2_PHASE = 0.0, 41 | parameter CLKOUT3_PHASE = 0.0, 42 | parameter CLKOUT4_PHASE = 0.0, 43 | parameter CLKOUT5_PHASE = 0.0, 44 | 45 | parameter DIVCLK_DIVIDE = 1, 46 | 47 | /* both not implemented */ 48 | parameter REF_JITTER1 = 0.0, 49 | parameter STARTUP_WAIT = "FALSE", 50 | 51 | /* Setting the FPGA model and speed grade allows a more realistic 52 | * simulation. Default values are the most restrictive */ 53 | parameter FPGA_TYPE = "ARTIX", 54 | parameter SPEED_GRADE = "-1")( 55 | output CLKOUT0, 56 | output CLKOUT1, 57 | output CLKOUT2, 58 | output CLKOUT3, 59 | output CLKOUT4, 60 | output CLKOUT5, 61 | /* PLL feedback output. */ 62 | output CLKFBOUT, 63 | 64 | output LOCKED, 65 | 66 | input CLKIN1, 67 | /* PLL feedback input. Is ignored in this implementation, but should be connected to CLKFBOUT for internal feedback. */ 68 | input CLKFBIN, 69 | 70 | /* Used to power down instatiated but unused PLLs */ 71 | input PWRDWN, 72 | input RST); 73 | 74 | wire [15:0] DO; 75 | wire DRDY; 76 | 77 | pll #( 78 | .BANDWIDTH(BANDWIDTH), 79 | .CLKFBOUT_MULT_F(CLKFBOUT_MULT), 80 | .CLKFBOUT_PHASE(CLKFBOUT_PHASE), 81 | .CLKIN1_PERIOD(CLKIN1_PERIOD), 82 | .CLKIN2_PERIOD(0.000), 83 | 84 | .CLKOUT0_DIVIDE_F(CLKOUT0_DIVIDE), 85 | .CLKOUT1_DIVIDE(CLKOUT1_DIVIDE), 86 | .CLKOUT2_DIVIDE(CLKOUT2_DIVIDE), 87 | .CLKOUT3_DIVIDE(CLKOUT3_DIVIDE), 88 | .CLKOUT4_DIVIDE(CLKOUT4_DIVIDE), 89 | .CLKOUT5_DIVIDE(CLKOUT5_DIVIDE), 90 | 91 | .CLKOUT0_DUTY_CYCLE(CLKOUT0_DUTY_CYCLE), 92 | .CLKOUT1_DUTY_CYCLE(CLKOUT1_DUTY_CYCLE), 93 | .CLKOUT2_DUTY_CYCLE(CLKOUT2_DUTY_CYCLE), 94 | .CLKOUT3_DUTY_CYCLE(CLKOUT3_DUTY_CYCLE), 95 | .CLKOUT4_DUTY_CYCLE(CLKOUT4_DUTY_CYCLE), 96 | .CLKOUT5_DUTY_CYCLE(CLKOUT5_DUTY_CYCLE), 97 | 98 | .CLKOUT0_PHASE(CLKOUT0_PHASE), 99 | .CLKOUT1_PHASE(CLKOUT1_PHASE), 100 | .CLKOUT2_PHASE(CLKOUT2_PHASE), 101 | .CLKOUT3_PHASE(CLKOUT3_PHASE), 102 | .CLKOUT4_PHASE(CLKOUT4_PHASE), 103 | .CLKOUT5_PHASE(CLKOUT5_PHASE), 104 | 105 | .DIVCLK_DIVIDE(DIVCLK_DIVIDE), 106 | .REF_JITTER1(REF_JITTER1), 107 | .REF_JITTER2(0.010), 108 | .STARTUP_WAIT(STARTUP_WAIT), 109 | .COMPENSATION("ZHOLD"), 110 | 111 | .MODULE_TYPE("PLLE2_BASE"), 112 | 113 | .FPGA_TYPE(FPGA_TYPE), 114 | .SPEED_GRADE(SPEED_GRADE)) 115 | plle2_base ( 116 | .CLKOUT0(CLKOUT0), 117 | .CLKOUT1(CLKOUT1), 118 | .CLKOUT2(CLKOUT2), 119 | .CLKOUT3(CLKOUT3), 120 | .CLKOUT4(CLKOUT4), 121 | .CLKOUT5(CLKOUT5), 122 | 123 | .CLKFBOUT(CLKFBOUT), 124 | .LOCKED(LOCKED), 125 | 126 | .CLKIN1(CLKIN1), 127 | .CLKIN2(1'b0), 128 | .CLKINSEL(1'b1), 129 | 130 | .PWRDWN(PWRDWN), 131 | .RST(RST), 132 | .CLKFBIN(CLKFBIN), 133 | 134 | .DADDR(7'h00), 135 | .DCLK(1'b0), 136 | .DEN(1'b0), 137 | .DWE(1'b0), 138 | .DI(16'h0), 139 | 140 | .DO(DO), 141 | .DRDY(DRDY) 142 | ); 143 | endmodule 144 | -------------------------------------------------------------------------------- /tb/freq_gen_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * freq_gen_tb.v: Test bench for freq_gen.v 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 1000 14 | `endif 15 | 16 | `ifndef M_1000 17 | `define M_1000 1000 18 | `endif 19 | 20 | `ifndef D 21 | `define D 1 22 | `endif 23 | 24 | `ifndef O_1000 25 | `define O_1000 1000 26 | `endif 27 | 28 | module freq_gen_tb (); 29 | reg RST; 30 | reg PWRDWN; 31 | reg period_stable; 32 | reg [31:0] ref_period_length_1000; 33 | reg clk; 34 | 35 | wire out; 36 | wire [31:0] out_period_length_1000; 37 | 38 | /* resets the high counter module used for frequency checks */ 39 | wire [31:0] highs_counted; 40 | integer pass_count; 41 | integer fail_count; 42 | 43 | /* adjust according to the number of test cases */ 44 | localparam total = 6; 45 | 46 | freq_gen dut ( 47 | .M_1000(`M_1000), 48 | .D(`D), 49 | .O_1000(`O_1000), 50 | .RST(RST), 51 | .PWRDWN(PWRDWN), 52 | .ref_period_1000(ref_period_length_1000), 53 | .clk(clk), 54 | .out(out), 55 | .out_period_length_1000(out_period_length_1000), 56 | .period_stable(period_stable)); 57 | 58 | high_counter high_counter ( 59 | .clk(out), 60 | .rst(~period_stable), 61 | .count(highs_counted)); 62 | 63 | 64 | initial begin 65 | $dumpfile("freq_gen_tb.vcd"); 66 | $dumpvars(0, freq_gen_tb); 67 | 68 | RST = 0; 69 | PWRDWN = 0; 70 | period_stable = 0; 71 | clk = 0; 72 | ref_period_length_1000 = 20 * 1000; 73 | 74 | pass_count = 0; 75 | fail_count = 0; 76 | 77 | #10; 78 | RST = 1; 79 | #10; 80 | 81 | if (out === 1'b0) begin 82 | $display("PASSED: RST"); 83 | pass_count = pass_count + 1; 84 | end else begin 85 | $display("FAILED: RST"); 86 | fail_count = fail_count + 1; 87 | end 88 | 89 | period_stable = 1; 90 | RST = 0; 91 | #((ref_period_length_1000 / 1000.0) + 11); 92 | if (out === 1'b1) begin 93 | $display("PASSED: rising edge detection"); 94 | pass_count = pass_count + 1; 95 | end else begin 96 | $display("FAILED: rising edge detection"); 97 | fail_count = fail_count + 1; 98 | end 99 | 100 | #(`WAIT_INTERVAL - ((ref_period_length_1000 / 1000.0) + 11)); 101 | /* use 1.0 to calculate floating point numbers */ 102 | if ($floor(`WAIT_INTERVAL / highs_counted) == $floor((ref_period_length_1000 / 1000.0) * ((`D * (`O_1000 / 1000.0) * 1.0) / (`M_1000 / 1000.0)))) begin 103 | $display("PASSED: ref period = 20"); 104 | pass_count = pass_count + 1; 105 | end else begin 106 | $display("FAILED: ref period = 20"); 107 | fail_count = fail_count + 1; 108 | end 109 | 110 | period_stable = 0; 111 | ref_period_length_1000 = 10 * 1000; 112 | #`WAIT_INTERVAL; 113 | period_stable = 1; 114 | #`WAIT_INTERVAL; 115 | if ($floor(`WAIT_INTERVAL / highs_counted) == $floor((ref_period_length_1000 / 1000.0) * ((`D * (`O_1000 / 1000.0) * 1.0) / (`M_1000 / 1000.0)))) begin 116 | $display("PASSED: ref period = 10"); 117 | pass_count = pass_count + 1; 118 | end else begin 119 | $display("FAILED: ref period = 10"); 120 | fail_count = fail_count + 1; 121 | end 122 | 123 | period_stable = 0; 124 | ref_period_length_1000 = 5600; 125 | #`WAIT_INTERVAL; 126 | period_stable = 1; 127 | #`WAIT_INTERVAL; 128 | if ($floor(`WAIT_INTERVAL / highs_counted) == $floor((ref_period_length_1000 / 1000.0) * ((`D * (`O_1000 / 1000.0) * 1.0) / (`M_1000 / 1000.0)))) begin 129 | $display("PASSED: ref period = 5.6"); 130 | pass_count = pass_count + 1; 131 | end else begin 132 | $display("FAILED: ref period = 5.6"); 133 | fail_count = fail_count + 1; 134 | end 135 | 136 | if ($floor((`WAIT_INTERVAL + (ref_period_length_1000 / 1000.0)) / highs_counted) == $floor(out_period_length_1000 / 1000.0)) begin 137 | $display("PASSED: period length output"); 138 | pass_count = pass_count + 1; 139 | end else begin 140 | $display("FAILED: period length output"); 141 | fail_count = fail_count + 1; 142 | end 143 | 144 | 145 | if ((pass_count + fail_count) == total) begin 146 | $display("PASSED: number of test cases"); 147 | pass_count = pass_count + 1; 148 | end else begin 149 | $display("FAILED: number of test cases"); 150 | fail_count = fail_count + 1; 151 | end 152 | 153 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 154 | $finish; 155 | end 156 | 157 | always #((ref_period_length_1000 / 1000.0) / 2.0) clk <= ~clk; 158 | endmodule 159 | -------------------------------------------------------------------------------- /mmcme2_base.v: -------------------------------------------------------------------------------- 1 | /* 2 | * mmcme2_base.v: Simulates the MMCME2_BASE pll of the Xilinx 7 series. This 3 | * is just a wrapper around the actual logic found in pll.v 4 | * author: Till Mahlburg 5 | * year: 2020 6 | * organization: Universität Leipzig 7 | * license: ISC 8 | * 9 | */ 10 | 11 | `timescale 1 ns / 1 ps 12 | 13 | /* A reference for the interface can be found in Xilinx UG953 page 461ff */ 14 | module MMCME2_BASE #( 15 | /* not implemented */ 16 | parameter BANDWIDTH = "OPTIMIZED", 17 | 18 | parameter CLKFBOUT_MULT_F = 5.000, 19 | parameter CLKFBOUT_PHASE = 0.000, 20 | 21 | /* is ignored, but should be set */ 22 | parameter CLKIN1_PERIOD = 0.000, 23 | 24 | parameter CLKOUT0_DIVIDE_F = 1.000, 25 | parameter CLKOUT1_DIVIDE = 1, 26 | parameter CLKOUT2_DIVIDE = 1, 27 | parameter CLKOUT3_DIVIDE = 1, 28 | parameter CLKOUT4_DIVIDE = 1, 29 | parameter CLKOUT5_DIVIDE = 1, 30 | parameter CLKOUT6_DIVIDE = 1, 31 | 32 | parameter CLKOUT0_DUTY_CYCLE = 0.500, 33 | parameter CLKOUT1_DUTY_CYCLE = 0.500, 34 | parameter CLKOUT2_DUTY_CYCLE = 0.500, 35 | parameter CLKOUT3_DUTY_CYCLE = 0.500, 36 | parameter CLKOUT4_DUTY_CYCLE = 0.500, 37 | parameter CLKOUT5_DUTY_CYCLE = 0.500, 38 | parameter CLKOUT6_DUTY_CYCLE = 0.500, 39 | 40 | parameter CLKOUT0_PHASE = 0.000, 41 | parameter CLKOUT1_PHASE = 0.000, 42 | parameter CLKOUT2_PHASE = 0.000, 43 | parameter CLKOUT3_PHASE = 0.000, 44 | parameter CLKOUT4_PHASE = 0.000, 45 | parameter CLKOUT5_PHASE = 0.000, 46 | parameter CLKOUT6_PHASE = 0.000, 47 | 48 | parameter CLKOUT4_CASCADE = "FALSE", 49 | 50 | parameter DIVCLK_DIVIDE = 1, 51 | 52 | /* both not implemented */ 53 | parameter REF_JITTER1 = 0.010, 54 | parameter STARTUP_WAIT = "FALSE", 55 | 56 | /* Setting the FPGA model and speed grade allows a more realistic 57 | * simulation. Default values are the most restrictive */ 58 | parameter FPGA_TYPE = "ARTIX", 59 | parameter SPEED_GRADE = "-1")( 60 | output CLKOUT0, 61 | output CLKOUT0B, 62 | output CLKOUT1, 63 | output CLKOUT1B, 64 | output CLKOUT2, 65 | output CLKOUT2B, 66 | output CLKOUT3, 67 | output CLKOUT3B, 68 | output CLKOUT4, 69 | output CLKOUT5, 70 | output CLKOUT6, 71 | /* PLL feedback output. */ 72 | output CLKFBOUT, 73 | output CLKFBOUTB, 74 | 75 | output LOCKED, 76 | 77 | input CLKIN1, 78 | /* PLL feedback input. Is ignored in this implementation, but should be connected to CLKFBOUT for internal feedback. */ 79 | input CLKFBIN, 80 | 81 | /* Used to power down instatiated but unused PLLs */ 82 | input PWRDWN, 83 | input RST); 84 | 85 | wire [15:0] DO; 86 | wire DRDY; 87 | 88 | pll #( 89 | .BANDWIDTH(BANDWIDTH), 90 | .CLKFBOUT_MULT_F(CLKFBOUT_MULT_F), 91 | .CLKFBOUT_PHASE(CLKFBOUT_PHASE), 92 | .CLKIN1_PERIOD(CLKIN1_PERIOD), 93 | .CLKIN2_PERIOD(0.000), 94 | 95 | .CLKOUT0_DIVIDE_F(CLKOUT0_DIVIDE_F), 96 | .CLKOUT1_DIVIDE(CLKOUT1_DIVIDE), 97 | .CLKOUT2_DIVIDE(CLKOUT2_DIVIDE), 98 | .CLKOUT3_DIVIDE(CLKOUT3_DIVIDE), 99 | .CLKOUT4_DIVIDE(CLKOUT4_DIVIDE), 100 | .CLKOUT5_DIVIDE(CLKOUT5_DIVIDE), 101 | .CLKOUT6_DIVIDE(CLKOUT6_DIVIDE), 102 | 103 | .CLKOUT0_DUTY_CYCLE(CLKOUT0_DUTY_CYCLE), 104 | .CLKOUT1_DUTY_CYCLE(CLKOUT1_DUTY_CYCLE), 105 | .CLKOUT2_DUTY_CYCLE(CLKOUT2_DUTY_CYCLE), 106 | .CLKOUT3_DUTY_CYCLE(CLKOUT3_DUTY_CYCLE), 107 | .CLKOUT4_DUTY_CYCLE(CLKOUT4_DUTY_CYCLE), 108 | .CLKOUT5_DUTY_CYCLE(CLKOUT5_DUTY_CYCLE), 109 | .CLKOUT6_DUTY_CYCLE(CLKOUT6_DUTY_CYCLE), 110 | 111 | .CLKOUT0_PHASE(CLKOUT0_PHASE), 112 | .CLKOUT1_PHASE(CLKOUT1_PHASE), 113 | .CLKOUT2_PHASE(CLKOUT2_PHASE), 114 | .CLKOUT3_PHASE(CLKOUT3_PHASE), 115 | .CLKOUT4_PHASE(CLKOUT4_PHASE), 116 | .CLKOUT5_PHASE(CLKOUT5_PHASE), 117 | .CLKOUT6_PHASE(CLKOUT6_PHASE), 118 | 119 | .CLKOUT4_CASCADE(CLKOUT4_CASCADE), 120 | 121 | .DIVCLK_DIVIDE(DIVCLK_DIVIDE), 122 | .REF_JITTER1(REF_JITTER1), 123 | .REF_JITTER2(0.010), 124 | .STARTUP_WAIT(STARTUP_WAIT), 125 | .COMPENSATION("ZHOLD"), 126 | 127 | .MODULE_TYPE("MMCME2_BASE"), 128 | 129 | .FPGA_TYPE(FPGA_TYPE), 130 | .SPEED_GRADE(SPEED_GRADE)) 131 | mmcme2_base ( 132 | .CLKOUT0(CLKOUT0), 133 | .CLKOUT0B(CLKOUT0B), 134 | .CLKOUT1(CLKOUT1), 135 | .CLKOUT1B(CLKOUT1B), 136 | .CLKOUT2(CLKOUT2), 137 | .CLKOUT2B(CLKOUT2B), 138 | .CLKOUT3(CLKOUT3), 139 | .CLKOUT3B(CLKOUT3B), 140 | .CLKOUT4(CLKOUT4), 141 | .CLKOUT5(CLKOUT5), 142 | .CLKOUT6(CLKOUT6), 143 | 144 | .CLKFBOUT(CLKFBOUT), 145 | .CLKFBOUTB(CLKFBOUTB), 146 | .LOCKED(LOCKED), 147 | 148 | .CLKIN1(CLKIN1), 149 | .CLKIN2(1'b0), 150 | .CLKINSEL(1'b1), 151 | 152 | .PWRDWN(PWRDWN), 153 | .RST(RST), 154 | .CLKFBIN(CLKFBIN), 155 | 156 | .DADDR(7'h00), 157 | .DCLK(1'b0), 158 | .DEN(1'b0), 159 | .DWE(1'b0), 160 | .DI(16'h0), 161 | 162 | .DO(DO), 163 | .DRDY(DRDY)); 164 | 165 | endmodule 166 | -------------------------------------------------------------------------------- /plle2_adv.v: -------------------------------------------------------------------------------- 1 | /* 2 | * plle2_adv.v: Simulates the PLLE2_ADV pll of the xilinx 7 series. This is 3 | * just a wrapper around the actual logic found in pll.v 4 | * author: Till Mahlburg 5 | * year: 2020 6 | * organization: Universität Leipzig 7 | * license: ISC 8 | * 9 | */ 10 | 11 | `timescale 1 ns / 1 ps 12 | 13 | /* A reference for the interface can be found in Xilinx UG953 page 503ff */ 14 | module PLLE2_ADV #( 15 | /* not implemented */ 16 | parameter BANDWIDTH = "OPTIMIZED", 17 | 18 | parameter CLKFBOUT_MULT = 5, 19 | parameter CLKFBOUT_PHASE = 0.0, 20 | 21 | /* are ignored, but need to be set */ 22 | parameter CLKIN1_PERIOD = 0.0, 23 | parameter CLKIN2_PERIOD = 0.0, 24 | 25 | parameter CLKOUT0_DIVIDE = 1, 26 | parameter CLKOUT1_DIVIDE = 1, 27 | parameter CLKOUT2_DIVIDE = 1, 28 | parameter CLKOUT3_DIVIDE = 1, 29 | parameter CLKOUT4_DIVIDE = 1, 30 | parameter CLKOUT5_DIVIDE = 1, 31 | 32 | parameter CLKOUT0_DUTY_CYCLE = 0.5, 33 | parameter CLKOUT1_DUTY_CYCLE = 0.5, 34 | parameter CLKOUT2_DUTY_CYCLE = 0.5, 35 | parameter CLKOUT3_DUTY_CYCLE = 0.5, 36 | parameter CLKOUT4_DUTY_CYCLE = 0.5, 37 | parameter CLKOUT5_DUTY_CYCLE = 0.5, 38 | 39 | parameter CLKOUT0_PHASE = 0.0, 40 | parameter CLKOUT1_PHASE = 0.0, 41 | parameter CLKOUT2_PHASE = 0.0, 42 | parameter CLKOUT3_PHASE = 0.0, 43 | parameter CLKOUT4_PHASE = 0.0, 44 | parameter CLKOUT5_PHASE = 0.0, 45 | 46 | parameter DIVCLK_DIVIDE = 1, 47 | 48 | /* not implemented */ 49 | parameter REF_JITTER1 = 0.010, 50 | parameter REF_JITTER2 = 0.010, 51 | parameter STARTUP_WAIT = "FALSE", 52 | parameter COMPENSATION = "ZHOLD", 53 | 54 | /* Setting the FPGA model and speed grade allows a more realistic 55 | * simulation. Default values are the most restrictive */ 56 | parameter FPGA_TYPE = "ARTIX", 57 | parameter SPEED_GRADE = "-1")( 58 | output CLKOUT0, 59 | output CLKOUT1, 60 | output CLKOUT2, 61 | output CLKOUT3, 62 | output CLKOUT4, 63 | output CLKOUT5, 64 | /* PLL feedback output. */ 65 | output CLKFBOUT, 66 | 67 | output LOCKED, 68 | 69 | input CLKIN1, 70 | input CLKIN2, 71 | /* Select input clk. 1 for CLKIN1, 0 for CLKIN2 */ 72 | input CLKINSEL, 73 | /* PLL feedback input. Is ignored in this implementation, but should be connected to CLKFBOUT for internal feedback. */ 74 | input CLKFBIN, 75 | 76 | /* Used to power down instatiated but unused PLLs */ 77 | input PWRDWN, 78 | input RST, 79 | 80 | /* Dynamic reconfiguration ports */ 81 | /* Address to write/read reconfiguration data to/from */ 82 | input [6:0] DADDR, 83 | /* CLK to drive the reconfiguration */ 84 | input DCLK, 85 | /* ENable the reconfiguration functionality */ 86 | input DEN, 87 | /* Enable Write access on the given address */ 88 | input DWE, 89 | /* What to write in the given ADDR */ 90 | input [15:0] DI, 91 | 92 | /* Content of the given address */ 93 | output [15:0] DO, 94 | /* Tells you, when the reconfiguration is done and ready for new inputs */ 95 | output DRDY); 96 | 97 | pll #( 98 | .BANDWIDTH(BANDWIDTH), 99 | .CLKFBOUT_MULT_F(CLKFBOUT_MULT), 100 | .CLKFBOUT_PHASE(CLKFBOUT_PHASE), 101 | .CLKIN1_PERIOD(CLKIN1_PERIOD), 102 | .CLKIN2_PERIOD(CLKIN2_PERIOD), 103 | 104 | .CLKOUT0_DIVIDE_F(CLKOUT0_DIVIDE), 105 | .CLKOUT1_DIVIDE(CLKOUT1_DIVIDE), 106 | .CLKOUT2_DIVIDE(CLKOUT2_DIVIDE), 107 | .CLKOUT3_DIVIDE(CLKOUT3_DIVIDE), 108 | .CLKOUT4_DIVIDE(CLKOUT4_DIVIDE), 109 | .CLKOUT5_DIVIDE(CLKOUT5_DIVIDE), 110 | 111 | .CLKOUT0_DUTY_CYCLE(CLKOUT0_DUTY_CYCLE), 112 | .CLKOUT1_DUTY_CYCLE(CLKOUT1_DUTY_CYCLE), 113 | .CLKOUT2_DUTY_CYCLE(CLKOUT2_DUTY_CYCLE), 114 | .CLKOUT3_DUTY_CYCLE(CLKOUT3_DUTY_CYCLE), 115 | .CLKOUT4_DUTY_CYCLE(CLKOUT4_DUTY_CYCLE), 116 | .CLKOUT5_DUTY_CYCLE(CLKOUT5_DUTY_CYCLE), 117 | 118 | .CLKOUT0_PHASE(CLKOUT0_PHASE), 119 | .CLKOUT1_PHASE(CLKOUT1_PHASE), 120 | .CLKOUT2_PHASE(CLKOUT2_PHASE), 121 | .CLKOUT3_PHASE(CLKOUT3_PHASE), 122 | .CLKOUT4_PHASE(CLKOUT4_PHASE), 123 | .CLKOUT5_PHASE(CLKOUT5_PHASE), 124 | 125 | .DIVCLK_DIVIDE(DIVCLK_DIVIDE), 126 | .REF_JITTER1(REF_JITTER1), 127 | .REF_JITTER2(REF_JITTER2), 128 | .STARTUP_WAIT(STARTUP_WAIT), 129 | .COMPENSATION(COMPENSATION), 130 | 131 | .MODULE_TYPE("PLLE2_ADV"), 132 | 133 | .FPGA_TYPE(FPGA_TYPE), 134 | .SPEED_GRADE(SPEED_GRADE)) 135 | plle2_adv ( 136 | .CLKOUT0(CLKOUT0), 137 | .CLKOUT1(CLKOUT1), 138 | .CLKOUT2(CLKOUT2), 139 | .CLKOUT3(CLKOUT3), 140 | .CLKOUT4(CLKOUT4), 141 | .CLKOUT5(CLKOUT5), 142 | 143 | .CLKFBOUT(CLKFBOUT), 144 | .LOCKED(LOCKED), 145 | 146 | .CLKIN1(CLKIN1), 147 | .CLKIN2(CLKIN2), 148 | .CLKINSEL(CLKINSEL), 149 | 150 | .PWRDWN(PWRDWN), 151 | .RST(RST), 152 | .CLKFBIN(CLKFBIN), 153 | 154 | .DADDR(DADDR), 155 | .DCLK(DCLK), 156 | .DEN(DEN), 157 | .DWE(DWE), 158 | .DI(DI), 159 | 160 | .DO(DO), 161 | .DRDY(DRDY) 162 | ); 163 | 164 | endmodule 165 | -------------------------------------------------------------------------------- /tb/Makefile: -------------------------------------------------------------------------------- 1 | VC = iverilog 2 | 3 | test: period_check_test freq_gen_test period_count_test high_counter_test phase_shift_test plle2_base_test plle2_adv_test dyn_reconf_test mmcme2_base_test 4 | 5 | build: period_check_build freq_gen_build freq_gen_build period_count_build high_counter_build phase_shift_build plle2_base_build pll_led_build plle2_adv_build dyn_reconf_build mmcme2_base_build 6 | 7 | test_pll: plle2_base_build plle2_adv_build mmcme2_base_test 8 | 9 | 10 | chained_pll_test: ../plle2_base.v ../dyn_reconf.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v chained_pll_tb.v 11 | - $(VC) ../plle2_base.v ../dyn_reconf.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v chained_pll_tb.v -o chained_pll_tb 12 | - vvp chained_pll_tb 13 | 14 | pll_led_build: ../pll_example/pll_example.srcs/sources_1/new/pll_led_tb.v ../pll_example/pll_example.srcs/sources_1/new/pll_led.v ../pll_example/pll_example.srcs/sources_1/new/divider_synth.v ../plle2_base.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v 15 | - $(VC) ../pll_example/pll_example.srcs/sources_1/new/pll_led_tb.v -DFF_NUM=2 ../pll_example/pll_example.srcs/sources_1/new/pll_led.v ../pll_example/pll_example.srcs/sources_1/new/divider_synth.v ../plle2_base.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v -o pll_led_tb 16 | pll_led_test: pll_led_build 17 | - vvp pll_led_tb 18 | 19 | pll_adv_example_build: ../pll_adv_example/pll_adv_example.srcs/sources_1/new/pll_adv_example_tb.v ../pll_adv_example/pll_adv_example.srcs/sources_1/new/pll_adv_example.v ../plle2_adv.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v 20 | - iverilog ../pll_adv_example/pll_adv_example.srcs/sources_1/new/pll_adv_example_tb.v ../pll_adv_example/pll_adv_example.srcs/sources_1/new/pll_adv_example.v ../plle2_adv.v ../freq_gen.v ../period_check.v ../period_count.v ../phase_shift.v ../pll.v ../dyn_reconf.v -o pll_adv_example_tb 21 | 22 | pll_adv_example_test: pll_adv_example_build 23 | - vvp pll_adv_example_tb 24 | period_check_build: ../period_check.v period_check_tb.v 25 | - $(VC) ../period_check.v period_check_tb.v -o period_check_tb 26 | period_check_test: period_check_build 27 | - vvp period_check_tb 28 | 29 | freq_gen_build: ../freq_gen.v ../high_counter.v freq_gen_tb.v 30 | - $(VC) ../freq_gen.v ../high_counter.v freq_gen_tb.v -o freq_gen_tb 31 | freq_gen_test: freq_gen_build 32 | - vvp freq_gen_tb 33 | 34 | period_count_build: ../period_count.v period_count_tb.v 35 | - $(VC) ../period_count.v period_count_tb.v -o period_count_tb 36 | period_count_test: period_count_build 37 | - vvp period_count_tb 38 | 39 | high_counter_build: ../high_counter.v high_counter_tb.v 40 | - $(VC) ../high_counter.v high_counter_tb.v -o high_counter_tb 41 | high_counter_test: high_counter_build 42 | - vvp high_counter_tb 43 | 44 | phase_shift_build: ../phase_shift.v phase_shift_tb.v 45 | - $(VC) ../phase_shift.v phase_shift_tb.v -o phase_shift_tb 46 | phase_shift_test: phase_shift_build 47 | - vvp phase_shift_tb 48 | 49 | plle2_base_build: ../plle2_base.v ../period_count.v ../period_check.v ../freq_gen.v ../phase_shift.v ../pll.v ../dyn_reconf.v ../phase_shift_check.v ../duty_cycle_check.v plle2_base_tb.v 50 | - $(VC) ../plle2_base.v ../period_check.v ../period_count.v ../freq_gen.v ../phase_shift.v ../pll.v ../dyn_reconf.v ../duty_cycle_check.v ../phase_shift_check.v plle2_base_tb.v -o plle2_base_tb 51 | plle2_base_test: plle2_base_build 52 | - vvp plle2_base_tb 53 | 54 | mmcme2_base_build: ../mmcme2_base.v ../period_count.v ../period_check.v ../freq_gen.v ../phase_shift.v ../pll.v ../dyn_reconf.v ../phase_shift_check.v ../duty_cycle_check.v mmcme2_base_tb.v 55 | - $(VC) ../mmcme2_base.v ../period_check.v ../period_count.v ../freq_gen.v ../phase_shift.v ../pll.v ../dyn_reconf.v ../duty_cycle_check.v ../phase_shift_check.v mmcme2_base_tb.v -o mmcme2_base_tb 56 | mmcme2_base_test: mmcme2_base_build 57 | - vvp mmcme2_base_tb 58 | 59 | plle2_adv_build: ../plle2_adv.v ../period_count.v ../period_check.v ../freq_gen.v ../phase_shift.v ../pll.v ../dyn_reconf.v ../duty_cycle_check.v ../phase_shift_check.v plle2_adv_tb.v 60 | - $(VC) ../plle2_adv.v ../period_check.v ../period_count.v ../freq_gen.v ../phase_shift.v ../dyn_reconf.v ../duty_cycle_check.v ../phase_shift_check.v plle2_adv_tb.v ../pll.v -o plle2_adv_tb 61 | plle2_adv_test: plle2_adv_build 62 | - vvp plle2_adv_tb 63 | 64 | dyn_reconf_build: ../dyn_reconf.v dyn_reconf_tb.v 65 | - $(VC) ../dyn_reconf.v dyn_reconf_tb.v -o dyn_reconf_tb 66 | dyn_reconf_test: dyn_reconf_build 67 | - vvp dyn_reconf_tb 68 | 69 | duty_cycle_check_build: ../duty_cycle_check.v duty_cycle_check_tb.v 70 | - $(VC) ../duty_cycle_check.v duty_cycle_check_tb.v -o duty_cycle_check_tb 71 | duty_cycle_check_test: duty_cycle_check_build 72 | - vvp duty_cycle_check_tb 73 | 74 | phase_shift_check_build: ../phase_shift_check.v ../phase_shift.v phase_shift_check_tb.v 75 | - $(VC) ../phase_shift_check.v ../phase_shift.v phase_shift_check_tb.v -o phase_shift_check_tb 76 | phase_shift_check_test: phase_shift_check_build 77 | - vvp phase_shift_check_tb 78 | 79 | clean: 80 | - rm *_tb 81 | - rm *_tb.vcd 82 | -------------------------------------------------------------------------------- /tb/phase_shift_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * phase_shift_tb.v: Testbench for phase_shift.v 3 | * author: Till Mahlburg 4 | * year: 2019 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 1000 14 | `endif 15 | 16 | module phase_shift_tb (); 17 | reg rst; 18 | reg PWRDWN; 19 | reg clk; 20 | reg signed [31:0] shift_1000; 21 | reg [31:0] clk_period_1000; 22 | reg [6:0] duty_cycle; 23 | 24 | wire lock; 25 | wire clk_shifted; 26 | 27 | reg shift_fail; 28 | reg duty_cycle_fail; 29 | 30 | integer pass_count; 31 | integer fail_count; 32 | /* adjust according to the number of testcases */ 33 | localparam total = 17; 34 | 35 | phase_shift dut( 36 | .RST(rst), 37 | .PWRDWN(PWRDWN), 38 | .clk(clk), 39 | .shift_1000(shift_1000), 40 | .clk_period_1000(clk_period_1000), 41 | .duty_cycle(duty_cycle), 42 | .lock(lock), 43 | .clk_shifted(clk_shifted)); 44 | 45 | initial begin 46 | $dumpfile("phase_shift_tb.vcd"); 47 | $dumpvars(0, phase_shift_tb); 48 | 49 | rst = 0; 50 | PWRDWN = 0; 51 | clk = 0; 52 | clk_period_1000 = 20000; 53 | 54 | shift_1000 = 10 * 1000; 55 | duty_cycle = 50; 56 | 57 | pass_count = 0; 58 | fail_count = 0; 59 | 60 | #10; 61 | if (lock === 1'bx) begin 62 | $display("PASSED: lock should not be set before first reset"); 63 | pass_count = pass_count + 1; 64 | end else begin 65 | $display("FAILED: lock should not be set before first reset"); 66 | fail_count = fail_count + 1; 67 | end 68 | 69 | rst = 1; 70 | #10; 71 | 72 | if (clk_shifted === 1'b0 && lock === 1'b0) begin 73 | $display("PASSED: rst"); 74 | pass_count = pass_count + 1; 75 | end else begin 76 | $display("FAILED: rst"); 77 | fail_count = fail_count + 1; 78 | end 79 | 80 | rst = 0; 81 | #`WAIT_INTERVAL; 82 | shift_fail = 0; 83 | #`WAIT_INTERVAL; 84 | if (!shift_fail && lock) begin 85 | $display("PASSED: shift = 10°"); 86 | pass_count = pass_count + 1; 87 | end else begin 88 | $display("FAILED: shift = 10°"); 89 | fail_count = fail_count + 1; 90 | end 91 | 92 | shift_1000 = 182 * 1000; 93 | clk_period_1000 = 5000; 94 | #`WAIT_INTERVAL; 95 | shift_fail = 0; 96 | #`WAIT_INTERVAL; 97 | if (!shift_fail && lock) begin 98 | $display("PASSED: shift = 182°"); 99 | pass_count = pass_count + 1; 100 | end else begin 101 | $display("FAILED: shift = 182°"); 102 | fail_count = fail_count + 1; 103 | end 104 | 105 | shift_1000 = 181 * 1000; 106 | #`WAIT_INTERVAL; 107 | shift_fail = 0; 108 | #`WAIT_INTERVAL; 109 | if (!shift_fail && lock) begin 110 | $display("PASSED: shift = 181°"); 111 | pass_count = pass_count + 1; 112 | end else begin 113 | $display("FAILED: shift = 181°"); 114 | fail_count = fail_count + 1; 115 | end 116 | 117 | shift_1000 = 180 * 1000; 118 | #`WAIT_INTERVAL; 119 | shift_fail = 0; 120 | #`WAIT_INTERVAL; 121 | if (!shift_fail && lock) begin 122 | $display("PASSED: shift = 180°"); 123 | pass_count = pass_count + 1; 124 | end else begin 125 | $display("FAILED: shift = 180°"); 126 | fail_count = fail_count + 1; 127 | end 128 | 129 | shift_1000 = 360 * 1000; 130 | #`WAIT_INTERVAL; 131 | shift_fail = 0; 132 | #`WAIT_INTERVAL; 133 | if (!shift_fail && lock) begin 134 | $display("PASSED: shift = 360°"); 135 | pass_count = pass_count + 1; 136 | end else begin 137 | $display("FAILED: shift = 360°"); 138 | fail_count = fail_count + 1; 139 | end 140 | 141 | shift_1000 = -10 * 1000; 142 | #`WAIT_INTERVAL; 143 | shift_fail = 0; 144 | #`WAIT_INTERVAL; 145 | if (!shift_fail && lock) begin 146 | $display("PASSED: shift = -10°"); 147 | pass_count = pass_count + 1; 148 | end else begin 149 | $display("FAILED: shift = -10°"); 150 | fail_count = fail_count + 1; 151 | end 152 | 153 | clk_period_1000 = 1000; 154 | shift_1000 = 0; 155 | duty_cycle = 50; 156 | #`WAIT_INTERVAL; 157 | shift_fail = 0; 158 | #`WAIT_INTERVAL; 159 | if (!shift_fail && lock) begin 160 | $display("PASSED: shift = 0°, clk period = 1"); 161 | pass_count = pass_count + 1; 162 | end else begin 163 | $display("FAILED: shift = 0°, clk period = 1"); 164 | fail_count = fail_count + 1; 165 | end 166 | 167 | clk_period_1000 = 5000; 168 | #40; 169 | shift_1000 = 0; 170 | duty_cycle_fail = 0; 171 | duty_cycle = 1; 172 | #`WAIT_INTERVAL; 173 | if (!duty_cycle_fail && lock) begin 174 | $display("PASSED: duty cycle = 1%"); 175 | pass_count = pass_count + 1; 176 | end else begin 177 | $display("FAILED: duty cycle = 1%"); 178 | fail_count = fail_count + 1; 179 | end 180 | 181 | duty_cycle_fail = 0; 182 | duty_cycle = 10; 183 | #`WAIT_INTERVAL; 184 | if (!duty_cycle_fail && lock) begin 185 | $display("PASSED: duty cycle = 10%"); 186 | pass_count = pass_count + 1; 187 | end else begin 188 | $display("FAILED: duty cycle = 10%"); 189 | fail_count = fail_count + 1; 190 | end 191 | 192 | duty_cycle_fail = 0; 193 | duty_cycle = 49; 194 | #`WAIT_INTERVAL; 195 | if (!duty_cycle_fail && lock) begin 196 | $display("PASSED: duty cycle = 49%"); 197 | pass_count = pass_count + 1; 198 | end else begin 199 | $display("FAILED: duty cycle = 49%"); 200 | fail_count = fail_count + 1; 201 | end 202 | 203 | duty_cycle_fail = 0; 204 | duty_cycle = 50; 205 | #`WAIT_INTERVAL; 206 | if (!duty_cycle_fail && lock) begin 207 | $display("PASSED: duty cycle = 50%"); 208 | pass_count = pass_count + 1; 209 | end else begin 210 | $display("FAILED: duty cycle = 50%"); 211 | fail_count = fail_count + 1; 212 | end 213 | 214 | duty_cycle_fail = 0; 215 | duty_cycle = 51; 216 | #`WAIT_INTERVAL; 217 | if (!duty_cycle_fail && lock) begin 218 | $display("PASSED: duty cycle = 51%"); 219 | pass_count = pass_count + 1; 220 | end else begin 221 | $display("FAILED: duty cycle = 51%"); 222 | fail_count = fail_count + 1; 223 | end 224 | 225 | duty_cycle_fail = 0; 226 | duty_cycle = 90; 227 | #`WAIT_INTERVAL; 228 | if (!duty_cycle_fail && lock) begin 229 | $display("PASSED: duty cycle = 90%"); 230 | pass_count = pass_count + 1; 231 | end else begin 232 | $display("FAILED: duty cycle = 90%"); 233 | fail_count = fail_count + 1; 234 | end 235 | 236 | duty_cycle_fail = 0; 237 | duty_cycle = 99; 238 | #`WAIT_INTERVAL; 239 | if (!duty_cycle_fail && lock) begin 240 | $display("PASSED: duty cycle = 99%"); 241 | pass_count = pass_count + 1; 242 | end else begin 243 | $display("FAILED: duty cycle = 99%"); 244 | fail_count = fail_count + 1; 245 | end 246 | 247 | PWRDWN = 1'b1; 248 | #((clk_period_1000 / 2000.0) + 1); 249 | if (clk_shifted === 1'bx && lock === 1'bx) begin 250 | $display("PASSED: PWRDWN"); 251 | pass_count = pass_count + 1; 252 | end else begin 253 | $display("FAILED: PWRDWN"); 254 | fail_count = fail_count + 1; 255 | end 256 | 257 | if ((pass_count + fail_count) == total) begin 258 | $display("PASSED: number of test cases"); 259 | pass_count = pass_count + 1; 260 | end else begin 261 | $display("FAILED: number of test cases"); 262 | fail_count = fail_count + 1; 263 | end 264 | 265 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 266 | $finish; 267 | end 268 | 269 | always #((clk_period_1000 / 1000.0) / 2.0) clk <= ~clk; 270 | 271 | /* check if the phase shift failed */ 272 | always @(posedge clk or posedge rst) begin 273 | if (rst) begin 274 | shift_fail <= 0; 275 | end else begin 276 | if (shift_1000 >= 0) begin 277 | #(((shift_1000 / 1000.0) * ((clk_period_1000 / 1000.0) / 360.0)) - 0.1); 278 | if (clk_shifted != 0) begin 279 | shift_fail <= 1; 280 | end 281 | end else begin 282 | #(((clk_period_1000 / 1000.0) + ((shift_1000 / 1000.0) * ((clk_period_1000 / 1000.0) / 360.0))) - 0.1); 283 | if (clk_shifted != 0) begin 284 | shift_fail <= 1; 285 | end 286 | end 287 | 288 | #0.2; 289 | if (clk_shifted != 1) begin 290 | shift_fail <= 1; 291 | end 292 | end 293 | end 294 | 295 | /* check if duty cycle is correct */ 296 | 297 | always @(posedge clk_shifted or posedge rst) begin 298 | if (rst) begin 299 | duty_cycle_fail <= 0; 300 | end else begin 301 | #(((clk_period_1000 / 1000.0) * (duty_cycle / 100.0)) - 0.1); 302 | if (clk_shifted != 1) begin 303 | duty_cycle_fail <= 1; 304 | end 305 | #0.2; 306 | if (clk != 0) begin 307 | duty_cycle_fail <= 1; 308 | end 309 | end 310 | end 311 | 312 | endmodule 313 | -------------------------------------------------------------------------------- /tb/test_mmcme2_base.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class Mmcme2BaseTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the mmcme2_base.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_mmcme2_base_test(self, 25 | wait_interval=500, 26 | bandwidth=r'\"OPTIMIZED\"', 27 | 28 | clkfbout_mult_f=5.000, 29 | clkfbout_phase=0.000, 30 | 31 | clkin1_period=5.000, 32 | 33 | clkout0_divide_f=1.000, 34 | clkout1_divide=1, 35 | clkout2_divide=1, 36 | clkout3_divide=1, 37 | clkout4_divide=1, 38 | clkout5_divide=1, 39 | clkout6_divide=1, 40 | 41 | clkout0_duty_cycle=0.500, 42 | clkout1_duty_cycle=0.500, 43 | clkout2_duty_cycle=0.500, 44 | clkout3_duty_cycle=0.500, 45 | clkout4_duty_cycle=0.500, 46 | clkout5_duty_cycle=0.500, 47 | clkout6_duty_cycle=0.500, 48 | 49 | clkout0_phase=0.000, 50 | clkout1_phase=0.000, 51 | clkout2_phase=0.000, 52 | clkout3_phase=0.000, 53 | clkout4_phase=0.000, 54 | clkout5_phase=0.000, 55 | clkout6_phase=0.000, 56 | 57 | clkout4_cascade=r'\"FALSE\"', 58 | 59 | divclk_divide=1, 60 | 61 | ref_jitter1=0.010, 62 | startup_wait=r'\"FALSE\"'): 63 | """test mmcme2 base""" 64 | 65 | test_files = ["mmcme2_base_tb.v"] 66 | src_files = ["phase_shift.v", 67 | "mmcme2_base.v", 68 | "freq_gen.v", 69 | "period_check.v", 70 | "period_count.v", 71 | "dyn_reconf.v", 72 | "pll.v", 73 | "phase_shift_check.v", 74 | "duty_cycle_check.v"] 75 | # copy source files so we know that all required files are there 76 | self.copy_sources(test_files, src_files) 77 | 78 | verilog_files = test_files + src_files 79 | 80 | sim_res = self.simulate(verilog_files, 81 | """ 82 | -DWAIT_INTERVAL={} \ 83 | 84 | -DBANDWIDTH={} \ 85 | 86 | -DCLKFBOUT_MULT_F={} \ 87 | -DCLKFBOUT_PHASE={} \ 88 | 89 | -DCLKIN1_PERIOD={} \ 90 | 91 | -DCLKOUT0_DIVIDE_F={} \ 92 | -DCLKOUT1_DIVIDE={} \ 93 | -DCLKOUT2_DIVIDE={} \ 94 | -DCLKOUT3_DIVIDE={} \ 95 | -DCLKOUT4_DIVIDE={} \ 96 | -DCLKOUT5_DIVIDE={} \ 97 | -DCLKOUT6_DIVIDE={} \ 98 | 99 | -DCLKOUT0_DUTY_CYCLE={} \ 100 | -DCLKOUT1_DUTY_CYCLE={} \ 101 | -DCLKOUT2_DUTY_CYCLE={} \ 102 | -DCLKOUT3_DUTY_CYCLE={} \ 103 | -DCLKOUT4_DUTY_CYCLE={} \ 104 | -DCLKOUT5_DUTY_CYCLE={} \ 105 | -DCLKOUT6_DUTY_CYCLE={} \ 106 | 107 | -DCLKOUT0_PHASE={} \ 108 | -DCLKOUT1_PHASE={} \ 109 | -DCLKOUT2_PHASE={} \ 110 | -DCLKOUT3_PHASE={} \ 111 | -DCLKOUT4_PHASE={} \ 112 | -DCLKOUT5_PHASE={} \ 113 | -DCLKOUT6_PHASE={} \ 114 | 115 | -DCLKOUT4_CASCADE={} \ 116 | 117 | -DDIVCLK_DIVIDE={} \ 118 | 119 | -DREF_JITTER1={} \ 120 | -DSTARTUP_WAIT={}""" 121 | .format( 122 | wait_interval, 123 | 124 | bandwidth, 125 | 126 | clkfbout_mult_f, 127 | clkfbout_phase, 128 | 129 | clkin1_period, 130 | 131 | clkout0_divide_f, 132 | clkout1_divide, 133 | clkout2_divide, 134 | clkout3_divide, 135 | clkout4_divide, 136 | clkout5_divide, 137 | clkout6_divide, 138 | 139 | clkout0_duty_cycle, 140 | clkout1_duty_cycle, 141 | clkout2_duty_cycle, 142 | clkout3_duty_cycle, 143 | clkout4_duty_cycle, 144 | clkout5_duty_cycle, 145 | clkout6_duty_cycle, 146 | 147 | clkout0_phase, 148 | clkout1_phase, 149 | clkout2_phase, 150 | clkout3_phase, 151 | clkout4_phase, 152 | clkout5_phase, 153 | clkout6_phase, 154 | 155 | clkout4_cascade, 156 | 157 | divclk_divide, 158 | 159 | ref_jitter1, 160 | 161 | startup_wait) 162 | .replace("\n", "").replace("\t", "")) 163 | sim_output = sim_res.stdout_text 164 | 165 | # save vcd for analysis 166 | shutil.copy("mmcme2_base_tb.vcd", self.outputdir) 167 | 168 | self.check_test_bench_output(sim_output, 32) 169 | 170 | def test_mmcme2_base_default(self): 171 | """ 172 | :avocado: tags:quick,verilog 173 | """ 174 | 175 | self.generic_mmcme2_base_test() 176 | 177 | def test_mmcme2_base_clkout0(self): 178 | """ 179 | :avocado: tags:quick,verilog 180 | """ 181 | 182 | self.generic_mmcme2_base_test(clkout0_divide_f=2.5, 183 | clkout0_duty_cycle=0.6) 184 | 185 | def test_mmcme2_base_clkfbout(self): 186 | """ 187 | :avocado: tags:quick,verilog 188 | """ 189 | 190 | self.generic_mmcme2_base_test(clkfbout_mult_f=5.5, 191 | clkfbout_phase=10, 192 | clkin1_period=5.5) 193 | 194 | def test_mmcme2_base_clkout6(self): 195 | """ 196 | :avocado: tags:quick,verilog 197 | """ 198 | 199 | self.generic_mmcme2_base_test(clkout6_divide=8, 200 | clkout6_duty_cycle=0.125) 201 | 202 | def test_mmcme2_base_clkout6_phase(self): 203 | """ 204 | :avocado: tags:quick,verilog 205 | """ 206 | 207 | self.generic_mmcme2_base_test(clkout6_phase=-11.111) 208 | 209 | def test_mmcme2_base_clkout4_cascade(self): 210 | """ 211 | :avocado: tags:quick,verilog 212 | """ 213 | 214 | self.generic_mmcme2_base_test(clkout4_cascade=r'\"TRUE\"', 215 | clkout4_divide=5, 216 | clkout6_divide=10) 217 | -------------------------------------------------------------------------------- /tb/test_plle2_base.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class Plle2BaseTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the plle2_base.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_plle2_base_test(self, 25 | wait_interval=500, 26 | bandwidth=r'\"OPTIMIZED\"', 27 | 28 | clkfbout_mult=5, 29 | clkfbout_phase=0.000, 30 | 31 | clkin1_period=5.000, 32 | 33 | clkout0_divide=1, 34 | clkout1_divide=1, 35 | clkout2_divide=1, 36 | clkout3_divide=1, 37 | clkout4_divide=1, 38 | clkout5_divide=1, 39 | 40 | clkout0_duty_cycle=0.500, 41 | clkout1_duty_cycle=0.500, 42 | clkout2_duty_cycle=0.500, 43 | clkout3_duty_cycle=0.500, 44 | clkout4_duty_cycle=0.500, 45 | clkout5_duty_cycle=0.500, 46 | 47 | clkout0_phase=0.000, 48 | clkout1_phase=0.000, 49 | clkout2_phase=0.000, 50 | clkout3_phase=0.000, 51 | clkout4_phase=0.000, 52 | clkout5_phase=0.000, 53 | 54 | divclk_divide=1, 55 | 56 | ref_jitter1=0.010, 57 | startup_wait=r'\"FALSE\"'): 58 | """test plle2 base""" 59 | 60 | test_files = ["plle2_base_tb.v"] 61 | src_files = ["phase_shift.v", 62 | "plle2_base.v", 63 | "freq_gen.v", 64 | "period_check.v", 65 | "period_count.v", 66 | "dyn_reconf.v", 67 | "pll.v", 68 | "phase_shift_check.v", 69 | "duty_cycle_check.v"] 70 | # copy source files so we know that all required files are there 71 | self.copy_sources(test_files, src_files) 72 | 73 | verilog_files = test_files + src_files 74 | 75 | sim_res = self.simulate(verilog_files, 76 | """ 77 | -DWAIT_INTERVAL={} \ 78 | 79 | -DBANDWIDTH={} \ 80 | 81 | -DCLKFBOUT_MULT={} \ 82 | -DCLKFBOUT_PHASE={} \ 83 | 84 | -DCLKIN1_PERIOD={} \ 85 | 86 | -DCLKOUT0_DIVIDE={} \ 87 | -DCLKOUT1_DIVIDE={} \ 88 | -DCLKOUT2_DIVIDE={} \ 89 | -DCLKOUT3_DIVIDE={} \ 90 | -DCLKOUT4_DIVIDE={} \ 91 | -DCLKOUT5_DIVIDE={} \ 92 | 93 | -DCLKOUT0_DUTY_CYCLE={} \ 94 | -DCLKOUT1_DUTY_CYCLE={} \ 95 | -DCLKOUT2_DUTY_CYCLE={} \ 96 | -DCLKOUT3_DUTY_CYCLE={} \ 97 | -DCLKOUT4_DUTY_CYCLE={} \ 98 | -DCLKOUT5_DUTY_CYCLE={} \ 99 | 100 | -DCLKOUT0_PHASE={} \ 101 | -DCLKOUT1_PHASE={} \ 102 | -DCLKOUT2_PHASE={} \ 103 | -DCLKOUT3_PHASE={} \ 104 | -DCLKOUT4_PHASE={} \ 105 | -DCLKOUT5_PHASE={} \ 106 | 107 | -DDIVCLK_DIVIDE={} \ 108 | 109 | -DREF_JITTER1={} \ 110 | -DSTARTUP_WAIT={}""" 111 | .format( 112 | wait_interval, 113 | 114 | bandwidth, 115 | 116 | clkfbout_mult, 117 | clkfbout_phase, 118 | 119 | clkin1_period, 120 | 121 | clkout0_divide, 122 | clkout1_divide, 123 | clkout2_divide, 124 | clkout3_divide, 125 | clkout4_divide, 126 | clkout5_divide, 127 | 128 | clkout0_duty_cycle, 129 | clkout1_duty_cycle, 130 | clkout2_duty_cycle, 131 | clkout3_duty_cycle, 132 | clkout4_duty_cycle, 133 | clkout5_duty_cycle, 134 | 135 | clkout0_phase, 136 | clkout1_phase, 137 | clkout2_phase, 138 | clkout3_phase, 139 | clkout4_phase, 140 | clkout5_phase, 141 | 142 | divclk_divide, 143 | 144 | ref_jitter1, 145 | 146 | startup_wait) 147 | .replace("\n", "").replace("\t", "")) 148 | sim_output = sim_res.stdout_text 149 | 150 | # save vcd for analysis 151 | shutil.copy("plle2_base_tb.vcd", self.outputdir) 152 | 153 | self.check_test_bench_output(sim_output, 24) 154 | 155 | def test_plle2_base_default(self): 156 | """ 157 | :avocado: tags:quick,verilog 158 | """ 159 | 160 | self.generic_plle2_base_test() 161 | 162 | def test_plle2_base_clkfbout_clkin1_divclk(self): 163 | """ 164 | :avocado: tags:quick,verilog 165 | """ 166 | 167 | self.generic_plle2_base_test( 168 | clkfbout_mult=64, clkin1_period=20.000, divclk_divide=4) 169 | 170 | def test_plle2_base_clkout0(self): 171 | """ 172 | :avocado: tags:quick,verilog 173 | """ 174 | 175 | self.generic_plle2_base_test( 176 | clkout0_divide=5, clkout0_duty_cycle=0.100) 177 | 178 | def test_plle2_base_clkout5(self): 179 | """ 180 | :avocado: tags:quick,verilog 181 | """ 182 | 183 | self.generic_plle2_base_test( 184 | clkout5_divide=10, clkout5_duty_cycle=0.85) 185 | 186 | def test_plle2_base_clkout1_clkout2_divclk_clkfb(self): 187 | """ 188 | :avocado: tags:quick,verilog 189 | """ 190 | 191 | self.generic_plle2_base_test(clkout1_divide=3, 192 | clkout1_duty_cycle=0.450, 193 | clkout2_divide=8, 194 | clkout2_duty_cycle=0.333, 195 | divclk_divide=2, 196 | clkfbout_mult=8) 197 | 198 | def test_plle2_base_phase(self): 199 | """ 200 | :avocado: tags:quick, verilog 201 | """ 202 | 203 | self.generic_plle2_base_test(clkfbout_phase=90, 204 | clkout0_phase=22.5, 205 | clkout1_phase=45, 206 | clkout2_phase=90, 207 | clkout3_phase=180, 208 | clkout4_phase=270, 209 | clkout5_phase=-60) 210 | 211 | def test_plle2_base_showcase(self): 212 | """ 213 | :avocado: tags:verilog 214 | """ 215 | 216 | self.generic_plle2_base_test(clkout0_divide=2, 217 | clkout1_duty_cycle=0.1, 218 | clkout2_duty_cycle=0.9, 219 | clkout3_phase=180, 220 | clkout4_phase=-90, 221 | clkout5_divide=4, 222 | clkout5_duty_cycle=0.3, 223 | clkfbout_mult=4) 224 | -------------------------------------------------------------------------------- /dyn_reconf.v: -------------------------------------------------------------------------------- 1 | /* 2 | * dyn_reconf.v: Handles the reconfiguration process of the pll. 3 | * author: Till Mahlburg 4 | * year: 2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | /* TODO: - check RESERVED bits for change (not allowed in the actual PLL) 11 | * - FiltReg (Device dependent) 12 | * - PowerReg: How does it work? According to documentation, it 13 | * should be completely set to high for DRP, but how is it set to 14 | * without DRP? 15 | * - LockReg (Device dependent) 16 | * - DivReg: What purpose does edge have here? 17 | * - MMCME: FRAC(ClkReg2) 18 | */ 19 | 20 | `timescale 1 ns / 1 ps 21 | 22 | module dyn_reconf ( 23 | input RST, 24 | input PWRDWN, 25 | 26 | input [6:0] DADDR, 27 | input DCLK, 28 | input DEN, 29 | input DWE, 30 | input [15:0] DI, 31 | 32 | /* needed for internal calculation 33 | * is equal to clkfb period after lock is achieved */ 34 | input [31:0] vco_period_1000, 35 | 36 | output reg [15:0] DO, 37 | output reg DRDY, 38 | 39 | output reg [31:0] CLKOUT0_DIVIDE, 40 | output reg [31:0] CLKOUT1_DIVIDE, 41 | output reg [31:0] CLKOUT2_DIVIDE, 42 | output reg [31:0] CLKOUT3_DIVIDE, 43 | output reg [31:0] CLKOUT4_DIVIDE, 44 | output reg [31:0] CLKOUT5_DIVIDE, 45 | output reg [31:0] CLKOUT6_DIVIDE, 46 | 47 | output reg [31:0] CLKOUT0_DUTY_CYCLE_1000, 48 | output reg [31:0] CLKOUT1_DUTY_CYCLE_1000, 49 | output reg [31:0] CLKOUT2_DUTY_CYCLE_1000, 50 | output reg [31:0] CLKOUT3_DUTY_CYCLE_1000, 51 | output reg [31:0] CLKOUT4_DUTY_CYCLE_1000, 52 | output reg [31:0] CLKOUT5_DUTY_CYCLE_1000, 53 | output reg [31:0] CLKOUT6_DUTY_CYCLE_1000, 54 | 55 | output reg [31:0] CLKOUT0_PHASE, 56 | output reg [31:0] CLKOUT1_PHASE, 57 | output reg [31:0] CLKOUT2_PHASE, 58 | output reg [31:0] CLKOUT3_PHASE, 59 | output reg [31:0] CLKOUT4_PHASE, 60 | output reg [31:0] CLKOUT5_PHASE, 61 | output reg [31:0] CLKOUT6_PHASE, 62 | 63 | output reg [31:0] CLKFBOUT_MULT_F_1000, 64 | output reg [31:0] CLKFBOUT_PHASE, 65 | 66 | output reg [31:0] DIVCLK_DIVIDE); 67 | 68 | wire [31:0] CLKOUT_DIVIDE[0:6]; 69 | wire [31:0] CLKOUT_DUTY_CYCLE[0:6]; 70 | wire [31:0] CLKOUT_PHASE[0:6]; 71 | wire [31:0] CLKFBOUT_MULT_; 72 | wire [31:0] CLKFBOUT_PHASE_; 73 | wire [31:0] DIVCLK_DIVIDE_; 74 | 75 | /* registers for dynamic output reconfiguration */ 76 | reg [15:0] ClkReg1[0:6]; 77 | reg [15:0] ClkReg1_FB; 78 | 79 | reg [15:0] ClkReg2[0:6]; 80 | reg [15:0] ClkReg2_FB; 81 | 82 | reg [15:0] DivReg; 83 | 84 | reg [15:0] LockReg[1:3]; 85 | 86 | reg [15:0] FiltReg[1:2]; 87 | 88 | reg [15:0] PowerReg; 89 | 90 | genvar i; 91 | 92 | generate 93 | for (i = 0; i <= 6; i = i + 1) begin : generate_attributes 94 | assign CLKOUT_DIVIDE[i] = ClkReg1[i][11:6] + ClkReg1[i][5:0]; 95 | assign CLKOUT_DUTY_CYCLE[i] = ((ClkReg1[i][11:6] + (ClkReg2[i][7] / 2.0)) / (ClkReg1[i][11:6] + ClkReg1[i][5:0])) * 1000; 96 | assign CLKOUT_PHASE[i] = ((((vco_period_1000 / 1000.0) / 8.0) * ClkReg1[i][15:13]) + ((vco_period_1000 / 1000.0) * ClkReg2[i][5:0])); 97 | end 98 | endgenerate 99 | 100 | assign CLKFBOUT_MULT_ = ClkReg1_FB[11:6] + ClkReg1_FB[5:0]; 101 | assign CLKFBOUT_PHASE_ = ((((vco_period_1000 / 1000.0) / 8) * ClkReg1_FB[15:13]) + ((vco_period_1000 / 1000.0) * ClkReg2_FB[5:0])); 102 | assign DIVCLK_DIVIDE_ = DivReg[11:6] + DivReg[5:0]; 103 | 104 | always @(posedge DCLK or posedge RST or posedge PWRDWN) begin 105 | if (PWRDWN) begin 106 | DRDY <= 1'bx; 107 | DO <= 16'hXXXX; 108 | end else if (RST) begin 109 | DO <= 16'h0000; 110 | 111 | ClkReg1[0] <= 0; 112 | ClkReg1[1] <= 0; 113 | ClkReg1[2] <= 0; 114 | ClkReg1[3] <= 0; 115 | ClkReg1[4] <= 0; 116 | ClkReg1[5] <= 0; 117 | ClkReg1[6] <= 0; 118 | 119 | ClkReg2[0] <= 0; 120 | ClkReg2[1] <= 0; 121 | ClkReg2[2] <= 0; 122 | ClkReg2[3] <= 0; 123 | ClkReg2[4] <= 0; 124 | ClkReg2[5] <= 0; 125 | ClkReg2[6] <= 0; 126 | 127 | ClkReg1_FB <= 0; 128 | ClkReg2_FB <= 0; 129 | 130 | DivReg <= 0; 131 | 132 | LockReg[1] <= 0; 133 | LockReg[2] <= 0; 134 | LockReg[3] <= 0; 135 | 136 | PowerReg <= 16'h1111; 137 | FiltReg[1] <= 0; 138 | FiltReg[2] <= 0; 139 | 140 | DRDY <= 1'b1; 141 | end else if (DEN) begin 142 | DRDY <= 1'b0; 143 | /* Write */ 144 | if (DWE) begin 145 | case (DADDR) 146 | 7'h06 : ClkReg1[5] <= DI; 147 | 7'h07 : ClkReg2[5] <= DI; 148 | 7'h08 : ClkReg1[0] <= DI; 149 | 7'h09 : ClkReg2[0] <= DI; 150 | 7'h0A : ClkReg1[1] <= DI; 151 | 7'h0B : ClkReg2[1] <= DI; 152 | 7'h0C : ClkReg1[2] <= DI; 153 | 7'h0D : ClkReg2[2] <= DI; 154 | 7'h0E : ClkReg1[3] <= DI; 155 | 7'h0F : ClkReg2[3] <= DI; 156 | 7'h10 : ClkReg1[4] <= DI; 157 | 7'h11 : ClkReg2[4] <= DI; 158 | 7'h12 : ClkReg1[6] <= DI; 159 | 7'h13 : ClkReg2[6] <= DI; 160 | 7'h14 : ClkReg1_FB <= DI; 161 | 7'h15 : ClkReg2_FB <= DI; 162 | 7'h16 : DivReg <= DI; 163 | 7'h18 : begin 164 | LockReg[1] <= DI; 165 | $display("This register has no functionality yet."); 166 | end 167 | 7'h19 : begin 168 | LockReg[2] <= DI; 169 | $display("This register has no functionality yet."); 170 | end 171 | 7'h1A : begin 172 | LockReg[3] <= DI; 173 | $display("This register has no functionality yet."); 174 | end 175 | 7'h28 : begin 176 | PowerReg <= DI; 177 | $display("This register has no functionality yet."); 178 | end 179 | 7'h4E : begin 180 | FiltReg[1] <= DI; 181 | $display("This register has no functionality yet."); 182 | end 183 | 7'h4F : begin 184 | FiltReg[2] <= DI; 185 | $display("This register has no functionality yet."); 186 | end 187 | default : begin 188 | $display("There is no register under this address."); 189 | $finish; 190 | end 191 | endcase 192 | /* Read */ 193 | end else begin 194 | case (DADDR) 195 | 7'h06 : DO <= ClkReg1[5]; 196 | 7'h07 : DO <= ClkReg2[5]; 197 | 7'h08 : DO <= ClkReg1[0]; 198 | 7'h09 : DO <= ClkReg2[0]; 199 | 7'h0A : DO <= ClkReg1[1]; 200 | 7'h0B : DO <= ClkReg2[1]; 201 | 7'h0C : DO <= ClkReg1[2]; 202 | 7'h0D : DO <= ClkReg2[2]; 203 | 7'h0E : DO <= ClkReg1[3]; 204 | 7'h0F : DO <= ClkReg2[3]; 205 | 7'h10 : DO <= ClkReg1[4]; 206 | 7'h11 : DO <= ClkReg2[4]; 207 | 7'h12 : DO <= ClkReg1[6]; 208 | 7'h13 : DO <= ClkReg2[6]; 209 | 7'h14 : DO <= ClkReg1_FB; 210 | 7'h15 : DO <= ClkReg2_FB; 211 | 7'h16 : DO <= DivReg; 212 | 7'h18 : DO <= LockReg[1]; 213 | 7'h19 : DO <= LockReg[2]; 214 | 7'h1A : DO <= LockReg[3]; 215 | 7'h28 : DO <= PowerReg; 216 | 7'h4E : DO <= FiltReg[1]; 217 | 7'h4F : DO <= FiltReg[2]; 218 | default : begin 219 | $display("There is no register under this address."); 220 | $finish; 221 | end 222 | endcase 223 | end 224 | end else if (DRDY == 1'b0) begin 225 | DO <= 0; 226 | 227 | /* PHASE */ 228 | if (ClkReg2_FB[6]) begin 229 | CLKFBOUT_MULT_F_1000 <= 1000; 230 | end else begin 231 | CLKFBOUT_MULT_F_1000 <= CLKFBOUT_MULT_ * 1000; 232 | end 233 | 234 | /* MX */ 235 | if (ClkReg2_FB[9:8] == 2'b00) begin 236 | CLKFBOUT_PHASE <= CLKFBOUT_PHASE_; 237 | end else begin 238 | $display("MX has to be 2'b00."); 239 | $finish(); 240 | end 241 | 242 | /* NO COUNT */ 243 | if (DivReg[12]) begin 244 | DIVCLK_DIVIDE <= 1; 245 | end else begin 246 | DIVCLK_DIVIDE <= DIVCLK_DIVIDE_; 247 | end 248 | 249 | /* MX */ 250 | if (ClkReg2[0][9:8] == 2'b00) 251 | CLKOUT0_PHASE <= CLKOUT_PHASE[0]; 252 | else begin 253 | $display("MX has to be 2'b00."); 254 | $finish(); 255 | end 256 | if (ClkReg2[1][9:8] == 2'b00) 257 | CLKOUT1_PHASE <= CLKOUT_PHASE[1]; 258 | else begin 259 | $display("MX has to be 2'b00."); 260 | $finish(); 261 | end 262 | if (ClkReg2[2][9:8] == 2'b00) 263 | CLKOUT2_PHASE <= CLKOUT_PHASE[2]; 264 | else begin 265 | $display("MX has to be 2'b00."); 266 | $finish(); 267 | end 268 | if (ClkReg2[3][9:8] == 2'b00) 269 | CLKOUT3_PHASE <= CLKOUT_PHASE[3]; 270 | else begin 271 | $display("MX has to be 2'b00."); 272 | $finish(); 273 | end 274 | if (ClkReg2[4][9:8] == 2'b00) 275 | CLKOUT4_PHASE <= CLKOUT_PHASE[4]; 276 | else begin 277 | $display("MX has to be 2'b00."); 278 | $finish(); 279 | end 280 | if (ClkReg2[5][9:8] == 2'b00) 281 | CLKOUT5_PHASE <= CLKOUT_PHASE[5]; 282 | else begin 283 | $display("MX has to be 2'b00."); 284 | $finish(); 285 | end 286 | if (ClkReg2[6][9:8] == 2'b00) 287 | CLKOUT6_PHASE <= CLKOUT_PHASE[6]; 288 | else begin 289 | $display("MX has to be 2'b00."); 290 | $finish(); 291 | end 292 | 293 | /* NO COUNT */ 294 | if (ClkReg2[0][6]) begin 295 | CLKOUT0_DIVIDE <= 1; 296 | CLKOUT0_DUTY_CYCLE_1000 <= 0.5 * 1000; 297 | end else begin 298 | CLKOUT0_DIVIDE <= CLKOUT_DIVIDE[0]; 299 | CLKOUT0_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[0]; 300 | end 301 | 302 | if (ClkReg2[1][6]) begin 303 | CLKOUT1_DIVIDE <= 1; 304 | CLKOUT1_DUTY_CYCLE_1000 <= 0.5 * 1000; 305 | end else begin 306 | CLKOUT1_DIVIDE <= CLKOUT_DIVIDE[1]; 307 | CLKOUT1_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[0]; 308 | end 309 | 310 | if (ClkReg2[2][6]) begin 311 | CLKOUT2_DIVIDE <= 1; 312 | CLKOUT2_DUTY_CYCLE_1000 <= 0.5 * 1000; 313 | end else begin 314 | CLKOUT2_DIVIDE <= CLKOUT_DIVIDE[2]; 315 | CLKOUT2_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[2]; 316 | end 317 | 318 | if (ClkReg2[3][6]) begin 319 | CLKOUT3_DIVIDE <= 1; 320 | CLKOUT3_DUTY_CYCLE_1000 <= 0.5 * 1000; 321 | end else begin 322 | CLKOUT3_DIVIDE <= CLKOUT_DIVIDE[3]; 323 | CLKOUT3_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[3]; 324 | end 325 | 326 | if (ClkReg2[4][6]) begin 327 | CLKOUT4_DIVIDE <= 1; 328 | CLKOUT4_DUTY_CYCLE_1000 <= 0.5 * 1000; 329 | end else begin 330 | CLKOUT4_DIVIDE <= CLKOUT_DIVIDE[4]; 331 | CLKOUT4_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[4]; 332 | end 333 | 334 | if (ClkReg2[4][6]) begin 335 | CLKOUT4_DIVIDE <= 1; 336 | CLKOUT4_DUTY_CYCLE_1000 <= 0.5 * 1000; 337 | end else begin 338 | CLKOUT4_DIVIDE <= CLKOUT_DIVIDE[4]; 339 | CLKOUT4_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[4]; 340 | end 341 | 342 | if (ClkReg2[5][6]) begin 343 | CLKOUT5_DIVIDE <= 1; 344 | CLKOUT5_DUTY_CYCLE_1000 <= 0.5 * 1000; 345 | end else begin 346 | CLKOUT5_DIVIDE <= CLKOUT_DIVIDE[5]; 347 | CLKOUT5_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[5]; 348 | end 349 | 350 | if (ClkReg2[6][6]) begin 351 | CLKOUT6_DIVIDE <= 1; 352 | CLKOUT6_DUTY_CYCLE_1000 <= 0.5 * 1000; 353 | end else begin 354 | CLKOUT6_DIVIDE <= CLKOUT_DIVIDE[6]; 355 | CLKOUT6_DUTY_CYCLE_1000 <= CLKOUT_DUTY_CYCLE[6]; 356 | end 357 | DRDY <= 1'b1; 358 | end 359 | end 360 | 361 | 362 | endmodule 363 | -------------------------------------------------------------------------------- /tb/plle2_base_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * plle2_base_tb.v: Testbench for plle2_base.v 3 | * author: Till Mahlburg 4 | * year: 2019-2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | `ifndef WAIT_INTERVAL 12 | `define WAIT_INTERVAL 1000 13 | `endif 14 | 15 | /* define all attributes as macros, so different combinations can be tested 16 | * more easily. 17 | * By default these are the given default values of the part. 18 | */ 19 | 20 | /* not implemented */ 21 | `ifndef BANDWIDTH 22 | `define BANDWIDTH "OPTIMIZED" 23 | `endif 24 | `ifndef CLKFBOUT_MULT 25 | `define CLKFBOUT_MULT 5 26 | `endif 27 | `ifndef CLKFBOUT_PHASE 28 | `define CLKFBOUT_PHASE 0.000 29 | `endif 30 | /* This deviates from the default values, because it is required to be set */ 31 | `ifndef CLKIN1_PERIOD 32 | `define CLKIN1_PERIOD 5.000 33 | `endif 34 | 35 | `ifndef CLKOUT0_DIVIDE 36 | `define CLKOUT0_DIVIDE 1 37 | `endif 38 | `ifndef CLKOUT1_DIVIDE 39 | `define CLKOUT1_DIVIDE 1 40 | `endif 41 | `ifndef CLKOUT2_DIVIDE 42 | `define CLKOUT2_DIVIDE 1 43 | `endif 44 | `ifndef CLKOUT2_DIVIDE 45 | `define CLKOUT2_DIVIDE 1 46 | `endif 47 | `ifndef CLKOUT3_DIVIDE 48 | `define CLKOUT3_DIVIDE 1 49 | `endif 50 | `ifndef CLKOUT4_DIVIDE 51 | `define CLKOUT4_DIVIDE 1 52 | `endif 53 | `ifndef CLKOUT5_DIVIDE 54 | `define CLKOUT5_DIVIDE 1 55 | `endif 56 | 57 | `ifndef CLKOUT0_DUTY_CYCLE 58 | `define CLKOUT0_DUTY_CYCLE 0.500 59 | `endif 60 | `ifndef CLKOUT1_DUTY_CYCLE 61 | `define CLKOUT1_DUTY_CYCLE 0.500 62 | `endif 63 | `ifndef CLKOUT2_DUTY_CYCLE 64 | `define CLKOUT2_DUTY_CYCLE 0.500 65 | `endif 66 | `ifndef CLKOUT3_DUTY_CYCLE 67 | `define CLKOUT3_DUTY_CYCLE 0.500 68 | `endif 69 | `ifndef CLKOUT4_DUTY_CYCLE 70 | `define CLKOUT4_DUTY_CYCLE 0.500 71 | `endif 72 | `ifndef CLKOUT5_DUTY_CYCLE 73 | `define CLKOUT5_DUTY_CYCLE 0.500 74 | `endif 75 | 76 | `ifndef CLKOUT0_PHASE 77 | `define CLKOUT0_PHASE 0.000 78 | `endif 79 | `ifndef CLKOUT1_PHASE 80 | `define CLKOUT1_PHASE 0.000 81 | `endif 82 | `ifndef CLKOUT2_PHASE 83 | `define CLKOUT2_PHASE 0.000 84 | `endif 85 | `ifndef CLKOUT3_PHASE 86 | `define CLKOUT3_PHASE 0.000 87 | `endif 88 | `ifndef CLKOUT4_PHASE 89 | `define CLKOUT4_PHASE 0.000 90 | `endif 91 | `ifndef CLKOUT5_PHASE 92 | `define CLKOUT5_PHASE 0.000 93 | `endif 94 | 95 | `ifndef DIVCLK_DIVIDE 96 | `define DIVCLK_DIVIDE 1 97 | `endif 98 | /* not implemented */ 99 | `ifndef REF_JITTER1 100 | `define REF_JITTER1 0.010 101 | `endif 102 | 103 | /* not implemented */ 104 | `ifndef STARTUP_WAIT 105 | `define STARTUP_WAIT "FALSE" 106 | `endif 107 | 108 | module PLLE2_BASE_tb(); 109 | wire CLKOUT[0:5]; 110 | 111 | wire CLKFBOUT; 112 | wire LOCKED; 113 | 114 | reg CLKIN1; 115 | 116 | reg PWRDWN; 117 | reg RST; 118 | wire CLKFBIN; 119 | 120 | integer pass_count; 121 | integer fail_count; 122 | /* change according to the number of test cases */ 123 | localparam total = 23; 124 | 125 | reg reset; 126 | wire [31:0] period_1000[0:5]; 127 | wire [31:0] period_1000_fb; 128 | 129 | wire dcc_fail[0:5]; 130 | wire dcc_fail_fb; 131 | 132 | wire psc_fail[0:5]; 133 | wire psc_fail_fb; 134 | 135 | wire [31:0] CLKOUT_DIVIDE[0:5]; 136 | assign CLKOUT_DIVIDE[0] = `CLKOUT0_DIVIDE; 137 | assign CLKOUT_DIVIDE[1] = `CLKOUT1_DIVIDE; 138 | assign CLKOUT_DIVIDE[2] = `CLKOUT2_DIVIDE; 139 | assign CLKOUT_DIVIDE[3] = `CLKOUT3_DIVIDE; 140 | assign CLKOUT_DIVIDE[4] = `CLKOUT4_DIVIDE; 141 | assign CLKOUT_DIVIDE[5] = `CLKOUT5_DIVIDE; 142 | 143 | wire [31:0] CLKOUT_DUTY_CYCLE_1000[0:5]; 144 | assign CLKOUT_DUTY_CYCLE_1000[0] = (`CLKOUT0_DUTY_CYCLE * 1000); 145 | assign CLKOUT_DUTY_CYCLE_1000[1] = (`CLKOUT1_DUTY_CYCLE * 1000); 146 | assign CLKOUT_DUTY_CYCLE_1000[2] = (`CLKOUT2_DUTY_CYCLE * 1000); 147 | assign CLKOUT_DUTY_CYCLE_1000[3] = (`CLKOUT3_DUTY_CYCLE * 1000); 148 | assign CLKOUT_DUTY_CYCLE_1000[4] = (`CLKOUT4_DUTY_CYCLE * 1000); 149 | assign CLKOUT_DUTY_CYCLE_1000[5] = (`CLKOUT5_DUTY_CYCLE * 1000); 150 | 151 | wire [31:0] CLKOUT_PHASE_1000[0:5]; 152 | assign CLKOUT_PHASE_1000[0] = (`CLKOUT0_PHASE * 1000); 153 | assign CLKOUT_PHASE_1000[1] = (`CLKOUT1_PHASE * 1000); 154 | assign CLKOUT_PHASE_1000[2] = (`CLKOUT2_PHASE * 1000); 155 | assign CLKOUT_PHASE_1000[3] = (`CLKOUT3_PHASE * 1000); 156 | assign CLKOUT_PHASE_1000[4] = (`CLKOUT4_PHASE * 1000); 157 | assign CLKOUT_PHASE_1000[5] = (`CLKOUT5_PHASE * 1000); 158 | 159 | /* instantiate PLLE2_BASE with default values for all the attributes */ 160 | PLLE2_BASE #( 161 | .BANDWIDTH(`BANDWIDTH), 162 | .CLKFBOUT_MULT(`CLKFBOUT_MULT), 163 | .CLKFBOUT_PHASE(`CLKFBOUT_PHASE), 164 | .CLKIN1_PERIOD(`CLKIN1_PERIOD), 165 | 166 | .CLKOUT0_DIVIDE(`CLKOUT0_DIVIDE), 167 | .CLKOUT1_DIVIDE(`CLKOUT1_DIVIDE), 168 | .CLKOUT2_DIVIDE(`CLKOUT2_DIVIDE), 169 | .CLKOUT3_DIVIDE(`CLKOUT3_DIVIDE), 170 | .CLKOUT4_DIVIDE(`CLKOUT4_DIVIDE), 171 | .CLKOUT5_DIVIDE(`CLKOUT5_DIVIDE), 172 | 173 | .CLKOUT0_DUTY_CYCLE(`CLKOUT0_DUTY_CYCLE), 174 | .CLKOUT1_DUTY_CYCLE(`CLKOUT1_DUTY_CYCLE), 175 | .CLKOUT2_DUTY_CYCLE(`CLKOUT2_DUTY_CYCLE), 176 | .CLKOUT3_DUTY_CYCLE(`CLKOUT3_DUTY_CYCLE), 177 | .CLKOUT4_DUTY_CYCLE(`CLKOUT4_DUTY_CYCLE), 178 | .CLKOUT5_DUTY_CYCLE(`CLKOUT5_DUTY_CYCLE), 179 | 180 | .CLKOUT0_PHASE(`CLKOUT0_PHASE), 181 | .CLKOUT1_PHASE(`CLKOUT1_PHASE), 182 | .CLKOUT2_PHASE(`CLKOUT2_PHASE), 183 | .CLKOUT3_PHASE(`CLKOUT3_PHASE), 184 | .CLKOUT4_PHASE(`CLKOUT4_PHASE), 185 | .CLKOUT5_PHASE(`CLKOUT5_PHASE), 186 | 187 | .DIVCLK_DIVIDE(`DIVCLK_DIVIDE), 188 | .REF_JITTER1(`REF_JITTER1), 189 | .STARTUP_WAIT(`STARTUP_WAIT)) 190 | dut ( 191 | .CLKOUT0(CLKOUT[0]), 192 | .CLKOUT1(CLKOUT[1]), 193 | .CLKOUT2(CLKOUT[2]), 194 | .CLKOUT3(CLKOUT[3]), 195 | .CLKOUT4(CLKOUT[4]), 196 | .CLKOUT5(CLKOUT[5]), 197 | 198 | .CLKFBOUT(CLKFBOUT), 199 | .LOCKED(LOCKED), 200 | 201 | .CLKIN1(CLKIN1), 202 | 203 | .PWRDWN(PWRDWN), 204 | .RST(RST), 205 | .CLKFBIN(CLKFBIN) 206 | ); 207 | 208 | genvar i; 209 | generate 210 | for (i = 0; i <= 5; i = i + 1) begin : period_count 211 | period_count period_count ( 212 | .RST(reset), 213 | .clk(CLKOUT[i]), 214 | .period_length_1000(period_1000[i])); 215 | end 216 | 217 | for (i = 0; i <= 5; i = i + 1) begin : dcc 218 | duty_cycle_check dcc ( 219 | .desired_duty_cycle_1000(CLKOUT_DUTY_CYCLE_1000[i]), 220 | .clk_period_1000((`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * CLKOUT_DIVIDE[i]) / `CLKFBOUT_MULT)) * 1000), 221 | .clk(CLKOUT[i]), 222 | .reset(reset), 223 | .LOCKED(LOCKED), 224 | .fail(dcc_fail[i])); 225 | end 226 | 227 | for (i = 0; i <= 5; i = i + 1) begin : psc 228 | phase_shift_check psc ( 229 | .desired_shift_1000(CLKOUT_PHASE_1000[i]), 230 | .clk_period_1000(1000 * `CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * CLKOUT_DIVIDE[i]) / `CLKFBOUT_MULT)), 231 | .clk_shifted(CLKOUT[i]), 232 | .clk(CLKFBOUT), 233 | .rst(RST), 234 | .LOCKED(LOCKED), 235 | .fail(psc_fail[i])); 236 | end 237 | endgenerate 238 | 239 | period_count period_count_fb ( 240 | .RST(reset), 241 | .clk(CLKFBOUT), 242 | .period_length_1000(period_1000_fb)); 243 | 244 | phase_shift_check pscfb ( 245 | .desired_shift_1000(`CLKFBOUT_PHASE * 1000), 246 | .clk_period_1000(`CLKIN1_PERIOD * (`DIVCLK_DIVIDE / `CLKFBOUT_MULT) * 1000), 247 | .clk_shifted(CLKFBOUT), 248 | .clk(CLKIN1), 249 | .rst(RST), 250 | .LOCKED(LOCKED), 251 | .fail(psc_fail_fb)); 252 | 253 | 254 | /* ------------ BEGIN TEST CASES ------------- */ 255 | /* default loop variable */ 256 | integer k; 257 | 258 | initial begin 259 | $dumpfile("plle2_base_tb.vcd"); 260 | $dumpvars(0, PLLE2_BASE_tb); 261 | 262 | pass_count = 0; 263 | fail_count = 0; 264 | reset = 0; 265 | 266 | CLKIN1 = 0; 267 | RST = 0; 268 | PWRDWN = 0; 269 | #10; 270 | reset = 1; 271 | RST = 1; 272 | #10; 273 | if ((CLKOUT[0] & CLKOUT[1] & CLKOUT[2] & CLKOUT[3] & CLKOUT[4] & CLKOUT[5] & CLKFBOUT & LOCKED) == 0) begin 274 | $display("PASSED: RST signal"); 275 | pass_count = pass_count + 1; 276 | end else begin 277 | $display("FAILED: RST signal"); 278 | fail_count = fail_count + 1; 279 | end 280 | reset = 0; 281 | RST = 0; 282 | /* Test for correct number of highs for the given parameters. 283 | * This is down for all six outputs and the feedback output. 284 | */ 285 | #`WAIT_INTERVAL; 286 | 287 | if (LOCKED === 1'b1) begin 288 | $display("PASSED: LOCKED"); 289 | pass_count = pass_count + 1; 290 | end else begin 291 | $display("FAILED: LOCKED"); 292 | fail_count = fail_count + 1; 293 | end 294 | 295 | 296 | /*------- FREQUENCY ---------*/ 297 | for (k = 0; k <= 5; k = k + 1) begin 298 | if ((period_1000[k] / 1000.0 == `CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * CLKOUT_DIVIDE[k] * 1.0) / `CLKFBOUT_MULT))) begin 299 | $display("PASSED: CLKOUT%0d frequency", k); 300 | pass_count = pass_count + 1; 301 | end else begin 302 | $display("FAILED: CLKOUT%0d frequency", k); 303 | fail_count = fail_count + 1; 304 | end 305 | end 306 | 307 | if ((period_1000_fb / 1000.0) == (`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * 1.0) / `CLKFBOUT_MULT))) begin 308 | $display("PASSED: CLKFBOUT frequency"); 309 | pass_count = pass_count + 1; 310 | end else begin 311 | $display("FAILED: CLKFBOUT frequency"); 312 | fail_count = fail_count + 1; 313 | end 314 | 315 | 316 | /*------- DUTY CYCLE ---------*/ 317 | for (k = 0; k <= 5; k = k + 1) begin 318 | if (dcc_fail[k] !== 1'b1) begin 319 | $display("PASSED: CLKOUT%0d duty cycle", k); 320 | pass_count = pass_count + 1; 321 | end else begin 322 | $display("FAILED: CLKOUT%0d duty cycle", k); 323 | fail_count = fail_count + 1; 324 | end 325 | end 326 | 327 | /*------- PHASE SHIFT ---------*/ 328 | for (k = 0; k <= 5; k = k + 1) begin 329 | if (psc_fail[k] !== 1'b1) begin 330 | $display("PASSED: CLKOUT%0d phase shift", k); 331 | pass_count = pass_count + 1; 332 | end else begin 333 | $display("FAILED: CLKOUT%0d phase shift", k); 334 | fail_count = fail_count + 1; 335 | end 336 | end 337 | 338 | if (psc_fail_fb !== 1'b1) begin 339 | $display("PASSED: CLKOUTFB phase shift"); 340 | pass_count = pass_count + 1; 341 | end else begin 342 | $display("FAILED: CLKOUTFB phase shift"); 343 | fail_count = fail_count + 1; 344 | end 345 | 346 | 347 | PWRDWN = 1; 348 | #100; 349 | if ((CLKOUT[0] & CLKOUT[1] & CLKOUT[2] & CLKOUT[3] & CLKOUT[4] & CLKOUT[5] & CLKFBOUT) === 1'bx) begin 350 | $display("PASSED: PWRDWN"); 351 | pass_count = pass_count + 1; 352 | end else begin 353 | $display("FAILED: PWRDWN"); 354 | fail_count = fail_count + 1; 355 | end 356 | 357 | if ((pass_count + fail_count) == total) begin 358 | $display("PASSED: number of test cases"); 359 | pass_count = pass_count + 1; 360 | end else begin 361 | $display("FAILED: number of test cases"); 362 | fail_count = fail_count + 1; 363 | end 364 | 365 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 366 | 367 | $finish; 368 | end 369 | 370 | /* connect CLKFBIN with CLKFBOUT to use internal feedback */ 371 | assign CLKFBIN = CLKFBOUT; 372 | 373 | always #(`CLKIN1_PERIOD / 2) CLKIN1 = ~CLKIN1; 374 | endmodule 375 | -------------------------------------------------------------------------------- /tb/test_plle2_adv.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | 3 | import shutil 4 | import os 5 | 6 | from avocado import Test 7 | 8 | 9 | class Plle2AdvTest(Test, test_base.Mixin): 10 | """ 11 | This class tests the plle2_adv.v module. 12 | """ 13 | 14 | def setUp(self): 15 | """ 16 | This function is needed in every test class. 17 | """ 18 | # main directory of the project 19 | self.project_dir = os.path.dirname(self.basedir) 20 | 21 | # change to workdir so simulation process find the source files 22 | os.chdir(self.workdir) 23 | 24 | def generic_plle2_adv_test(self, 25 | wait_interval=400, 26 | bandwidth=r'\"OPTIMIZED\"', 27 | 28 | clkfbout_mult=5, 29 | clkfbout_phase=0.000, 30 | 31 | clkin1_period=5.000, 32 | clkin2_period=4.000, 33 | 34 | clkout0_divide=1, 35 | clkout1_divide=1, 36 | clkout2_divide=1, 37 | clkout3_divide=1, 38 | clkout4_divide=1, 39 | clkout5_divide=1, 40 | 41 | clkout0_duty_cycle=0.500, 42 | clkout1_duty_cycle=0.500, 43 | clkout2_duty_cycle=0.500, 44 | clkout3_duty_cycle=0.500, 45 | clkout4_duty_cycle=0.500, 46 | clkout5_duty_cycle=0.500, 47 | 48 | clkout0_phase=0.000, 49 | clkout1_phase=0.000, 50 | clkout2_phase=0.000, 51 | clkout3_phase=0.000, 52 | clkout4_phase=0.000, 53 | clkout5_phase=0.000, 54 | 55 | divclk_divide=1, 56 | 57 | ref_jitter1=0.010, 58 | ref_jitter2=0.010, 59 | startup_wait=r'\"FALSE\"', 60 | compensation=r'\"ZHOLD\"', 61 | 62 | daddr1=r'7\'h08', 63 | daddr2=r'7\'h09', 64 | di1=r'16\'b011_0_000110_000011', 65 | di2=r'16\'b0_000_0_0_00_0_0_00011', 66 | dclk_period=2): 67 | """test plle2 adv""" 68 | 69 | test_files = ["plle2_adv_tb.v"] 70 | src_files = ["phase_shift.v", 71 | "plle2_adv.v", 72 | "freq_gen.v", 73 | "period_check.v", 74 | "period_count.v", 75 | "dyn_reconf.v", 76 | "pll.v", 77 | "phase_shift_check.v", 78 | "duty_cycle_check.v"] 79 | # copy source files so we know that all required files are there 80 | self.copy_sources(test_files, src_files) 81 | 82 | verilog_files = test_files + src_files 83 | 84 | sim_res = self.simulate(verilog_files, 85 | """ 86 | -DWAIT_INTERVAL={} \ 87 | 88 | -DBANDWIDTH={} \ 89 | 90 | -DCLKFBOUT_MULT={} \ 91 | -DCLKFBOUT_PHASE={} \ 92 | 93 | -DCLKIN1_PERIOD={} \ 94 | -DCLKIN2_PERIOD={} \ 95 | 96 | -DCLKOUT0_DIVIDE={} \ 97 | -DCLKOUT1_DIVIDE={} \ 98 | -DCLKOUT2_DIVIDE={} \ 99 | -DCLKOUT3_DIVIDE={} \ 100 | -DCLKOUT4_DIVIDE={} \ 101 | -DCLKOUT5_DIVIDE={} \ 102 | 103 | -DCLKOUT0_DUTY_CYCLE={} \ 104 | -DCLKOUT1_DUTY_CYCLE={} \ 105 | -DCLKOUT2_DUTY_CYCLE={} \ 106 | -DCLKOUT3_DUTY_CYCLE={} \ 107 | -DCLKOUT4_DUTY_CYCLE={} \ 108 | -DCLKOUT5_DUTY_CYCLE={} \ 109 | 110 | -DCLKOUT0_PHASE={} \ 111 | -DCLKOUT1_PHASE={} \ 112 | -DCLKOUT2_PHASE={} \ 113 | -DCLKOUT3_PHASE={} \ 114 | -DCLKOUT4_PHASE={} \ 115 | -DCLKOUT5_PHASE={} \ 116 | 117 | -DDIVCLK_DIVIDE={} \ 118 | 119 | -DREF_JITTER1={} \ 120 | -DREF_JITTER2={} \ 121 | -DSTARTUP_WAIT={} \ 122 | -DCOMPENSATION={} \ 123 | 124 | -DDADDR1={} \ 125 | -DDADDR2={} \ 126 | -DDI1={} \ 127 | -DDI2={} \ 128 | -DDCLK_PERIOD={}""" 129 | .format( 130 | wait_interval, 131 | 132 | bandwidth, 133 | 134 | clkfbout_mult, 135 | clkfbout_phase, 136 | 137 | clkin1_period, 138 | clkin2_period, 139 | 140 | clkout0_divide, 141 | clkout1_divide, 142 | clkout2_divide, 143 | clkout3_divide, 144 | clkout4_divide, 145 | clkout5_divide, 146 | 147 | clkout0_duty_cycle, 148 | clkout1_duty_cycle, 149 | clkout2_duty_cycle, 150 | clkout3_duty_cycle, 151 | clkout4_duty_cycle, 152 | clkout5_duty_cycle, 153 | 154 | clkout0_phase, 155 | clkout1_phase, 156 | clkout2_phase, 157 | clkout3_phase, 158 | clkout4_phase, 159 | clkout5_phase, 160 | 161 | divclk_divide, 162 | 163 | ref_jitter1, 164 | ref_jitter2, 165 | 166 | startup_wait, 167 | compensation, 168 | 169 | daddr1, 170 | daddr2, 171 | di1, 172 | di2, 173 | dclk_period) 174 | .replace("\n", "").replace("\t", "")) 175 | sim_output = sim_res.stdout_text 176 | 177 | # save vcd for analysis 178 | shutil.copy("plle2_adv_tb.vcd", self.outputdir) 179 | 180 | self.check_test_bench_output(sim_output, 48) 181 | 182 | def test_plle2_adv_default(self): 183 | """ 184 | :avocado: tags:quick,verilog 185 | """ 186 | self.generic_plle2_adv_test() 187 | 188 | def test_plle2_adv_clkfbout_clkin_divclk(self): 189 | """ 190 | :avocado: tags: quick,verilog 191 | """ 192 | self.generic_plle2_adv_test(clkfbout_mult=10, 193 | clkin1_period=6.000, 194 | clkin2_period=4.000, 195 | divclk_divide=2) 196 | 197 | def test_plle2_adv_clkout1_clkout2_clkout3(self): 198 | """ 199 | :avocado: tags:quick,verilog 200 | """ 201 | self.generic_plle2_adv_test(clkout1_divide=5, 202 | clkout1_duty_cycle=0.1, 203 | clkout2_divide=10, 204 | clkout2_duty_cycle=0.85, 205 | clkout3_divide=2, 206 | clkout3_duty_cycle=0.666) 207 | 208 | def test_plle2_adv_phase(self): 209 | """ 210 | :avocado: tags:quick,verilog 211 | """ 212 | self.generic_plle2_adv_test(clkfbout_phase=45, 213 | clkout0_phase=22.5, 214 | clkout1_phase=11.25, 215 | clkout2_phase=90, 216 | clkout3_phase=180, 217 | clkout4_phase=270, 218 | clkout5_phase=-45) 219 | 220 | def test_plle2_adv_clkout1_dyn_reconf(self): 221 | """ 222 | :avocado: tags:quick,verilog,dyn 223 | """ 224 | self.generic_plle2_adv_test(daddr1=r'7\'h0A', 225 | daddr2=r'7\'h0B', 226 | di1=r'16\'b000_0_001111_000101', 227 | di2=r'16\'b000000_00_1_0_000000') 228 | 229 | def test_plle2_adv_clkout2_phase_dyn_reconf(self): 230 | """ 231 | :avocado: tags:quick,verilog,dyn 232 | """ 233 | self.generic_plle2_adv_test(daddr1=r'7\'h0C', 234 | daddr2=r'7\'h0D', 235 | di1=r'16\'b100_0_000011_000011', 236 | di2=r'16\'b000000_00_0_1_000101') 237 | 238 | def test_plle2_adv_clkfbout_dyn_reconf(self): 239 | """ 240 | :avocado: tags:quick,verilog,dyn 241 | """ 242 | self.generic_plle2_adv_test(daddr1=r'7\'h14', 243 | daddr2=r'7\'h15', 244 | di1=r'16\'b000_0_000011_000001', 245 | di2=r'16\'b0_000_0_0_00_0_0_000000') 246 | 247 | def test_plle2_adv_clkfbout_phase_dyn_reconf(self): 248 | """ 249 | :avocado: tags:quick,verilog,dyn 250 | """ 251 | self.generic_plle2_adv_test(daddr1=r'7\'h14', 252 | daddr2=r'7\'h15', 253 | di1=r'16\'b010_0_000010_000010', 254 | di2=r'16\'b0_000_0_0_00_0_0_000111') 255 | 256 | def test_plle2_adv_divreg(self): 257 | """ 258 | :avocado: tags:quick,verilog,dyn 259 | """ 260 | self.generic_plle2_adv_test(daddr1=r'7\'h16', 261 | daddr2=r'7\'h4F', 262 | di1=r'16\'b00_1_0_0000011_000010', 263 | di2=0) 264 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xilinx 7 Series PLL and MMCM Simulation 2 | 3 | This project aims to simulate the behavior of the PLLE2_BASE as well as the PLLE2_ADV PLL and the MMCME2_BASE MMCM found on the Xilinx 7 Series FPGAs. The MMCME2_ADV MMCM is not (yet) supported. This is done in Verilog, and can for example be simulated using the Icarus Verilog simulation and synthesis tool. It follows the instantiation interface described in the [documentation](https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_3/ug953-vivado-7series-libraries.pdf) on page 509ff for the PLLs and 461ff for the MMCM. This way you can just drop the files listed below into your project, instantiate the PLL/MMCM like you would for real hardware and simulate it. Read on to learn how to use the module and what it can and cannot do. If you just want to know, what works and what doesn't, just have a look at the [project status](#status) 4 | 5 | ## Quickstart 6 | 7 | To use this module, you need to have the following files in your project: 8 | - ```plle2_base.v```, ```plle2_adv.v``` or ```mmcme2_base.v``` depending on [which one you want](#pll-choosing) 9 | - ```period_count.v``` 10 | - ```period_check.v``` 11 | - ```freq_gen.v``` 12 | - ```divider.v``` 13 | - ```phase_shift.v``` 14 | - ```dyn_reconf.v``` 15 | - ```pll.v``` 16 | 17 | 18 | To build and simulate your project, you can use [icarus verilog and vvp](http://iverilog.icarus.com/) and view the results in [GTKWave](http://gtkwave.sourceforge.net/): 19 | - ```iverilog plle2_base.v period_check.v period_count.v freq_gen.v divider.v phase_shift.v dyn_reconf.v pll.v -o ```, 20 | 21 | ```iverilog plle2_adv.v period_check.v period_count.v freq_gen.v divider.v phase_shift.v dyn_reconf.v pll.v -o ``` 22 | 23 | or ```iverilog mmcme2_base.v period_check.v period_count.v freq_gen.v divider.v phase_shift.v dyn_reconf.v pll.v -o ```, depending on [which one you want](#pll-choosing) 24 | - ```vvp ``` 25 | - ```gtkwave dump.vcd``` 26 | 27 | If you specified the name of your output file using something like ```$dumpfile("")```, you have to replace ```dump.vcd``` with your chosen name. 28 | 29 | The module works by supplying an input clock, which will be transformed to an, or rather 6 (or 7 in the case of the MMCM), output clocks. In the simplest case, this output clock depends on the input clock and multiple parameters. You can set the wanted output frequency, phase shift and duty cycle. The output frequency is calculated like this: ```output frequency = input frequency * (multiplier / (divider * output divider))```, while the output phase can be calculated (in relation to the input phase) by using this formula: ```output phase = feedback phase + output phase```. The parts of these formulas with "output" in their name are specific to one specific output, while the others are global. There are certain limits to the values. If you hit them, the module is going to stop simulation and inform you about it. Check out the [FAQ](#FAQ) section at the end to learn more about these limits. You can also find a table there, how the allowed VCO frequency depends on FPGA model, their speed grades the used module (MMCM or PLL). 30 | 31 | An typical instatiation of the PLLE2_BASE module might look like this: 32 | 33 | PLLE2_BASE #( 34 | .CLKFBOUT_MULT(8), // This multiplies your output clock by 8 35 | .CLKFBOUT_PHASE(90.0), // This shifts the output clock by 90 degrees 36 | .CLKIN1_PERIOD(10.0), // This specifies the period length of your input clock. This information is mandatory. 37 | 38 | // The following lines set up different dividers for every output 39 | .CLKOUT0_DIVIDE(128), 40 | .CLKOUT1_DIVIDE(64), 41 | .CLKOUT2_DIVIDE(32), 42 | .CLKOUT3_DIVIDE(16), 43 | .CLKOUT4_DIVIDE(128), 44 | .CLKOUT5_DIVIDE(128), 45 | 46 | // Similiarly you can set the duty cycle for every output 47 | .CLKOUT0_DUTY_CYCLE(0.5), 48 | .CLKOUT1_DUTY_CYCLE(0.5), 49 | .CLKOUT2_DUTY_CYCLE(0.5), 50 | .CLKOUT3_DUTY_CYCLE(0.5), 51 | .CLKOUT4_DUTY_CYCLE(0.9), 52 | .CLKOUT5_DUTY_CYCLE(0.1), 53 | 54 | // And the phase shift 55 | .CLKOUT0_PHASE(0.0), 56 | .CLKOUT1_PHASE(45.0), 57 | .CLKOUT2_PHASE(22.5), 58 | .CLKOUT3_PHASE(0.0), 59 | .CLKOUT4_PHASE(0.0), 60 | .CLKOUT5_PHASE(0.0), 61 | 62 | // You can also set up a divider for your input clock. This can be useful, if you have a very fast clock, which exceeds the limits of the PLL. 63 | .DIVCLK_DIVIDE(1), 64 | 65 | // At this point this instatiation differs from the real hardware. This is to allow setting a FPGA model and it's speed grade. This enables a more realistic simulation. It is, however, entirely optional. By default it is set to the most restrictive values (ARTIX -1), so it should work on every version. 66 | .FPGA_TYPE("ARTIX"), 67 | .SPEED_GRADE("-1")) 68 | pll ( 69 | // Bind the outputs of the PLL, for example like this. 70 | .CLKOUT0(output[0]), 71 | .CLKOUT1(output[1]), 72 | .CLKOUT2(output[2]), 73 | .CLKOUT3(output[3]), 74 | .CLKOUT4(output[5]), 75 | .CLKOUT5(output[6]), 76 | 77 | // These should always be set to the same wire. 78 | .CLKFBOUT(CLKFB), 79 | .CLKFBIN(CLKFB), 80 | 81 | // This informs you, if the output frequency is usable. 82 | .LOCKED(locked), 83 | // Bind your input clock. 84 | .CLKIN1(clk), 85 | 86 | // Allows you to power down or reset the PLL. 87 | .PWRDWN(pwrdwn), 88 | .RST(rst)); 89 | 90 | ## Example project 91 | 92 | An example project using the PLLE2_BASE found under ```pll_example/pll_example.srcs/sources_1/new/```. It is a simple program to show the usage of the module. It can be simulated from the ```tb/``` or the ```pll_example``` directory using 93 | 94 | - ```make pll_led_test``` 95 | 96 | This runs iverilog and vvp to simulate the module. 97 | To inspect the results you can use GTKWave like this: 98 | 99 | - ```gtkwave pll_led_tb.vcd``` 100 | 101 | The default values chosen are meant to be seen with the naked eye on real hardware. If you run the simulation using ```make``` the values are adjusted to be easy to see in GTKWave. 102 | 103 | There is also an example project using the PLLE2_ADV found under ```pll_adv_example/pll_example.srcs/sources_1/new``` It is similar to the other example, but uses the PLLE2_ADV module and it's dynamic reconfiguration capabilities. It can be simulated from the ```tb/``` or the ```pll_adv_example``` directory using 104 | 105 | - ```make pll_adv_example_test``` 106 | 107 | This runs iverilog and vvp to simulate the module. To inspect the results you can use GTKWave: 108 | 109 | - ```gtkwave pll_adv_example_tb.vcd``` 110 | 111 | 112 | To learn more about the instantiation of the module, you should read [Xilinx UG953](https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_3/ug953-vivado-7series-libraries.pdf) page 509ff. 113 | 114 |

Project Status

115 | 116 | ### Working 117 | - instantiation interface compatible to the one described in UG953 118 | - setting the phase, duty cycle and divider of CLKOUT outputs (CLKOUTn_DIVIDE, CLKOUTn_DUTY_CYCLE and CLKOUTn_PHASE) 119 | - lock detection (LOCKED) 120 | - PWRDWN and RST signals 121 | - setting DIVCLK_DIVIDE (divides the input clock) 122 | - tests for RST, PWRDWN, output frequency, output phase and output duty cycle 123 | - applying CLKFBOUT_MULT to multiply the output frequency 124 | - applying CLKFBOUT_PHASE to set a phase shift to every output 125 | - setting CLKINSEL and selecting one of two input clocks (PLLE2_ADV) 126 | - basic dynamic reconfiguration functionality (PLLE2_ADV) 127 | - CLKOUT6 (MMCME2_BASE) 128 | - CLKOUT0_DIVIDE_F for fractional divides (MMCME2_BASE) 129 | - CLKOUT0-3B for inverted outputs 130 | - CLKOUT4_CASCADE for using the divider of CLKOUT6 to divide the CLKOUT4 output again (MMCME2_BASE) 131 | - CLKFBOUT_MULT_F for fractional multipies (MMCME2_BASE) 132 | - stopping the simulation, if illegal values are hit 133 | - chaining PLLs should work as expected, although the CLKFBIN input is ignored 134 | 135 | ### Not Working 136 | - there is no feedback loop by design 137 | - BANDWIDTH, REF_JITTER1, REF_JITTER2, COMPENSATION and STARTUP_WAIT settings won't work with the current design approach 138 | - connecting CLKFBIN to any other clock than CLKFBOUT won't change the behaviour of the module 139 | - dynamic reconfiguration only has an effect in the ClkReg1 and ClkReg2 registers as well as the DivReg register (PLLE2_ADV) 140 | - RESERVED bits in the dynamic reconfiguration are ignored (PLLE2_ADV) 141 | 142 | ## Test 143 | 144 | You can test this project automatically using avocado or make. The testbenches themselves are written in pure verilog. 145 | 146 | ### Avocado [recommended] 147 | 148 | - install avocado: [Documentation](https://avocado-framework.readthedocs.io/en/latest/#how-to-install) 149 | - change into the ```tb/``` folder 150 | - run ```$ avocado run test_freq_gen.py test_high_counter.py test_period_check.py test_period_count.py test_phase_shift.py test_plle2_adv.py test_plle2_base.py test_mmcme2_base.py test_dyn_reconf.py``` 151 | 152 | ### Make 153 | 154 | - change into the ```tb/``` folder 155 | - run ```make```. This will just run every testbench in it's default configuration. 156 | 157 | ## Architecture 158 | 159 | This diagram roughly outlines the basic architecture of the project outlining the ```pll.v``` module, which has the ```plle2_base.v```, ```plle2_adv.v``` and ```mmcme2_base.v``` modules as wrappers determining the needed functionality. 160 | 161 | ![architecture diagram](https://raw.githubusercontent.com/ti-leipzig/sim-x-pll/master/arch.svg?sanitize=true) 162 | 163 | ## License 164 | 165 | This project is licensed under the [ISC license](https://github.com/ti-leipzig/sim-x-pll/blob/master/LICENSE). 166 | 167 |

FAQ

168 | 169 | ### What limits does the PLL/MMCM have? 170 | Use this table for parameters: 171 | 172 | | parameter | allowed values | 173 | | ------------------ | ----------------------------------------- | 174 | | BANDWIDTH | "OPTIMIZED", "HIGH", "LOW" | 175 | | CLKFBOUT_MULT | 2 - 64 | 176 | | CLKFBOUT_MULT_F | 2.000 - 64.000 | 177 | | CLKFBOUT_PHASE | -360.000 - 360.000 | 178 | | CLKINn_PERIOD | 0 - 52.631 | 179 | | CLKOUTn_DIVIDE | 1 - 128 | 180 | | CLKOUT0_DIVIDE_F | 1.000 - 128.000 | 181 | | CLKOUTn_DUTY_CYCLE | -360.000 - 360.000 | 182 | | CLKOUT4_CASCADE | "FALSE", "TRUE" | 183 | | DIVCLK_DIVIDE | 1 - 56 | 184 | | REF_JITTERn | 0.000 - 0.999 | 185 | | STARTUP_WAIT | "FALSE", "TRUE" | 186 | | COMPENSATION | "ZHOLD", "BUF_IN", "EXTERNAL", "INTERNAL" | 187 | | CLKOUT4_CASCADE | "TRUE", "FALSE" | 188 | 189 | Also there is a limitation in the PLL regarding the possible frequency. They depend on the capabilities of the VCO, which itself depends on the FPGA model, it's speedgrade and if it's used by a PLL or an MMCM. It's frequency can be calculated using this formula: ```VCO frequency = (CLKFBOUT_MULT * 1000) / (CLKIN1_PERIOD * DIVCLK_DIVIDE)```. Use this table for reference: 190 | 191 | | FPGA model | -3 | -2 | -2L | -2LE | -2LI | -2LG | -1 | -1LI | -1M | -1LM | -1Q | 192 | | ----------------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | 193 | | **ARTIX** - PLL | 800-2133 | 800-1866 | N/A | 800-1600 | N/A | N/A | 800-1600 | 800-1600 | N/A | N/A | N/A | 194 | | **ARTIX** - MMCM | 600-1600 | 600-1440 | N/A | 600-1200 | N/A | N/A | 600-1200 | 600-1200 | N/A | N/A | N/A | 195 | | **KINTEX** - PLL | 800-2133 | 800-1866 | N/A | 800-1600 | 800-1866 | N/A | 800-1600 | N/A | 800-1600 | 800-1600 | 800-1600 | 196 | | **KINTEX** - MMCM | 600-1600 | 600-1440 | N/A | 600-1200 | 600-1440 | N/A | 600-1200 | N/A | 600-1200 | 600-1200 | 600-1200 | 197 | | **VIRTEX** - PLL | 800-2133 | 800-1866 | 800-1866 | N/A | N/A | 800-1866 | 800-1600 | N/A | 800-1600 | N/A | N/A | 198 | | **VIRTEX** - MMCM | 600-1600 | 600-1440 | 600-1440 | N/A | N/A | 600-1440 | 600-1200 | N/A | 600-1200 | N/A | N/A | 199 | 200 | 201 |

Which PLL/MMCM should I choose?

202 | 203 | The main differences between the two PLL versions are the support for two input clocks and dynamic reconfiguration in PLLE2_ADV. For a more in-depth overview of the differences see [UGS472 page 70](https://www.xilinx.com/support/documentation/user_guides/ug472_7Series_Clocking.pdf). 204 | 205 | The MMCM offers an additional output (CLKOUT6), fractional divides for CLKOUT0 and CLKFBOUT (which functions as a fractional multiplier) and the possibility to use the divider of CLKOUT6 to divide CLKOUT4 again, allowing for divisors as high as 16384 (128 * 128). 206 | 207 | ### Is this module synthesizable? 208 | No, it isn't. This project is purely for simulation purposes. It is not meant to be synthesizable and contains a lot of unsynthesizable code. The [examples](https://github.com/ti-leipzig/sim-x-pll/tree/master/pll_example) however are synthesizable. 209 | -------------------------------------------------------------------------------- /pll_example/Basys3_Master.xdc: -------------------------------------------------------------------------------- 1 | ## This file is a general .xdc for the Basys3 rev B board 2 | ## To use it in a project: 3 | ## - uncomment the lines corresponding to used pins 4 | ## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project 5 | 6 | ## Clock signal 7 | set_property PACKAGE_PIN W5 [get_ports clk] 8 | set_property IOSTANDARD LVCMOS33 [get_ports clk] 9 | create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk] 10 | 11 | ## Switches 12 | #set_property PACKAGE_PIN V17 [get_ports {sw[0]}] 13 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[0]}] 14 | #set_property PACKAGE_PIN V16 [get_ports {sw[1]}] 15 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[1]}] 16 | #set_property PACKAGE_PIN W16 [get_ports {sw[2]}] 17 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[2]}] 18 | #set_property PACKAGE_PIN W17 [get_ports {sw[3]}] 19 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[3]}] 20 | #set_property PACKAGE_PIN W15 [get_ports {sw[4]}] 21 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[4]}] 22 | #set_property PACKAGE_PIN V15 [get_ports {sw[5]}] 23 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[5]}] 24 | #set_property PACKAGE_PIN W14 [get_ports {sw[6]}] 25 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[6]}] 26 | #set_property PACKAGE_PIN W13 [get_ports {sw[7]}] 27 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[7]}] 28 | #set_property PACKAGE_PIN V2 [get_ports {sw[8]}] 29 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[8]}] 30 | #set_property PACKAGE_PIN T3 [get_ports {sw[9]}] 31 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[9]}] 32 | #set_property PACKAGE_PIN T2 [get_ports {sw[10]}] 33 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[10]}] 34 | #set_property PACKAGE_PIN R3 [get_ports {sw[11]}] 35 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[11]}] 36 | #set_property PACKAGE_PIN W2 [get_ports {sw[12]}] 37 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[12]}] 38 | #set_property PACKAGE_PIN U1 [get_ports {sw[13]}] 39 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[13]}] 40 | #set_property PACKAGE_PIN T1 [get_ports {sw[14]}] 41 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[14]}] 42 | #set_property PACKAGE_PIN R2 [get_ports {sw[15]}] 43 | #set_property IOSTANDARD LVCMOS33 [get_ports {sw[15]}] 44 | 45 | 46 | # LEDs 47 | set_property PACKAGE_PIN U16 [get_ports {led[0]}] 48 | set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}] 49 | set_property PACKAGE_PIN E19 [get_ports {led[1]}] 50 | set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}] 51 | set_property PACKAGE_PIN U19 [get_ports {led[2]}] 52 | set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}] 53 | set_property PACKAGE_PIN V19 [get_ports {led[3]}] 54 | set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}] 55 | set_property PACKAGE_PIN W18 [get_ports {led[4]}] 56 | set_property IOSTANDARD LVCMOS33 [get_ports {led[4]}] 57 | set_property PACKAGE_PIN U15 [get_ports {led[5]}] 58 | set_property IOSTANDARD LVCMOS33 [get_ports {led[5]}] 59 | set_property PACKAGE_PIN U14 [get_ports {led[6]}] 60 | set_property IOSTANDARD LVCMOS33 [get_ports {led[6]}] 61 | set_property PACKAGE_PIN V14 [get_ports {led[7]}] 62 | set_property IOSTANDARD LVCMOS33 [get_ports {led[7]}] 63 | #set_property PACKAGE_PIN V13 [get_ports {led[8]}] 64 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[8]}] 65 | #set_property PACKAGE_PIN V3 [get_ports {led[9]}] 66 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[9]}] 67 | #set_property PACKAGE_PIN W3 [get_ports {led[10]}] 68 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[10]}] 69 | #set_property PACKAGE_PIN U3 [get_ports {led[11]}] 70 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[11]}] 71 | #set_property PACKAGE_PIN P3 [get_ports {led[12]}] 72 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[12]}] 73 | #set_property PACKAGE_PIN N3 [get_ports {led[13]}] 74 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[13]}] 75 | #set_property PACKAGE_PIN P1 [get_ports {led[14]}] 76 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[14]}] 77 | #set_property PACKAGE_PIN L1 [get_ports {led[15]}] 78 | #set_property IOSTANDARD LVCMOS33 [get_ports {led[15]}] 79 | 80 | 81 | ##7 segment display 82 | #set_property PACKAGE_PIN W7 [get_ports {seg[0]}] 83 | #set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}] 84 | #set_property PACKAGE_PIN W6 [get_ports {seg[1]}] 85 | #set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}] 86 | #set_property PACKAGE_PIN U8 [get_ports {seg[2]}] 87 | #set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}] 88 | #set_property PACKAGE_PIN V8 [get_ports {seg[3]}] 89 | #set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}] 90 | #set_property PACKAGE_PIN U5 [get_ports {seg[4]}] 91 | #set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}] 92 | #set_property PACKAGE_PIN V5 [get_ports {seg[5]}] 93 | #set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}] 94 | #set_property PACKAGE_PIN U7 [get_ports {seg[6]}] 95 | #set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}] 96 | 97 | #set_property PACKAGE_PIN V7 [get_ports dp] 98 | #set_property IOSTANDARD LVCMOS33 [get_ports dp] 99 | 100 | #set_property PACKAGE_PIN U2 [get_ports {an[0]}] 101 | #set_property IOSTANDARD LVCMOS33 [get_ports {an[0]}] 102 | #set_property PACKAGE_PIN U4 [get_ports {an[1]}] 103 | #set_property IOSTANDARD LVCMOS33 [get_ports {an[1]}] 104 | #set_property PACKAGE_PIN V4 [get_ports {an[2]}] 105 | #set_property IOSTANDARD LVCMOS33 [get_ports {an[2]}] 106 | #set_property PACKAGE_PIN W4 [get_ports {an[3]}] 107 | #set_property IOSTANDARD LVCMOS33 [get_ports {an[3]}] 108 | 109 | 110 | #Buttons 111 | set_property PACKAGE_PIN U18 [get_ports RST] 112 | set_property IOSTANDARD LVCMOS33 [get_ports RST] 113 | #set_property PACKAGE_PIN T18 [get_ports btnU] 114 | #set_property IOSTANDARD LVCMOS33 [get_ports btnU] 115 | #set_property PACKAGE_PIN W19 [get_ports btnL] 116 | #set_property IOSTANDARD LVCMOS33 [get_ports btnL] 117 | #set_property PACKAGE_PIN T17 [get_ports btnR] 118 | #set_property IOSTANDARD LVCMOS33 [get_ports btnR] 119 | #set_property PACKAGE_PIN U17 [get_ports btnD] 120 | #set_property IOSTANDARD LVCMOS33 [get_ports btnD] 121 | 122 | 123 | 124 | ##Pmod Header JA 125 | ##Sch name = JA1 126 | #set_property PACKAGE_PIN J1 [get_ports {JA[0]}] 127 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[0]}] 128 | ##Sch name = JA2 129 | #set_property PACKAGE_PIN L2 [get_ports {JA[1]}] 130 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[1]}] 131 | ##Sch name = JA3 132 | #set_property PACKAGE_PIN J2 [get_ports {JA[2]}] 133 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[2]}] 134 | ##Sch name = JA4 135 | #set_property PACKAGE_PIN G2 [get_ports {JA[3]}] 136 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[3]}] 137 | ##Sch name = JA7 138 | #set_property PACKAGE_PIN H1 [get_ports {JA[4]}] 139 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[4]}] 140 | ##Sch name = JA8 141 | #set_property PACKAGE_PIN K2 [get_ports {JA[5]}] 142 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[5]}] 143 | ##Sch name = JA9 144 | #set_property PACKAGE_PIN H2 [get_ports {JA[6]}] 145 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[6]}] 146 | ##Sch name = JA10 147 | #set_property PACKAGE_PIN G3 [get_ports {JA[7]}] 148 | #set_property IOSTANDARD LVCMOS33 [get_ports {JA[7]}] 149 | 150 | 151 | 152 | ##Pmod Header JB 153 | ##Sch name = JB1 154 | #set_property PACKAGE_PIN A14 [get_ports {JB[0]}] 155 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[0]}] 156 | ##Sch name = JB2 157 | #set_property PACKAGE_PIN A16 [get_ports {JB[1]}] 158 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[1]}] 159 | ##Sch name = JB3 160 | #set_property PACKAGE_PIN B15 [get_ports {JB[2]}] 161 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[2]}] 162 | ##Sch name = JB4 163 | #set_property PACKAGE_PIN B16 [get_ports {JB[3]}] 164 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[3]}] 165 | ##Sch name = JB7 166 | #set_property PACKAGE_PIN A15 [get_ports {JB[4]}] 167 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[4]}] 168 | ##Sch name = JB8 169 | #set_property PACKAGE_PIN A17 [get_ports {JB[5]}] 170 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[5]}] 171 | ##Sch name = JB9 172 | #set_property PACKAGE_PIN C15 [get_ports {JB[6]}] 173 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[6]}] 174 | ##Sch name = JB10 175 | #set_property PACKAGE_PIN C16 [get_ports {JB[7]}] 176 | #set_property IOSTANDARD LVCMOS33 [get_ports {JB[7]}] 177 | 178 | 179 | 180 | ##Pmod Header JC 181 | ##Sch name = JC1 182 | #set_property PACKAGE_PIN K17 [get_ports {JC[0]}] 183 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[0]}] 184 | ##Sch name = JC2 185 | #set_property PACKAGE_PIN M18 [get_ports {JC[1]}] 186 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[1]}] 187 | ##Sch name = JC3 188 | #set_property PACKAGE_PIN N17 [get_ports {JC[2]}] 189 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[2]}] 190 | ##Sch name = JC4 191 | #set_property PACKAGE_PIN P18 [get_ports {JC[3]}] 192 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[3]}] 193 | ##Sch name = JC7 194 | #set_property PACKAGE_PIN L17 [get_ports {JC[4]}] 195 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[4]}] 196 | ##Sch name = JC8 197 | #set_property PACKAGE_PIN M19 [get_ports {JC[5]}] 198 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[5]}] 199 | ##Sch name = JC9 200 | #set_property PACKAGE_PIN P17 [get_ports {JC[6]}] 201 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[6]}] 202 | ##Sch name = JC10 203 | #set_property PACKAGE_PIN R18 [get_ports {JC[7]}] 204 | #set_property IOSTANDARD LVCMOS33 [get_ports {JC[7]}] 205 | 206 | 207 | ##Pmod Header JXADC 208 | ##Sch name = XA1_P 209 | #set_property PACKAGE_PIN J3 [get_ports {JXADC[0]}] 210 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[0]}] 211 | ##Sch name = XA2_P 212 | #set_property PACKAGE_PIN L3 [get_ports {JXADC[1]}] 213 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[1]}] 214 | ##Sch name = XA3_P 215 | #set_property PACKAGE_PIN M2 [get_ports {JXADC[2]}] 216 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[2]}] 217 | ##Sch name = XA4_P 218 | #set_property PACKAGE_PIN N2 [get_ports {JXADC[3]}] 219 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[3]}] 220 | ##Sch name = XA1_N 221 | #set_property PACKAGE_PIN K3 [get_ports {JXADC[4]}] 222 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[4]}] 223 | ##Sch name = XA2_N 224 | #set_property PACKAGE_PIN M3 [get_ports {JXADC[5]}] 225 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[5]}] 226 | ##Sch name = XA3_N 227 | #set_property PACKAGE_PIN M1 [get_ports {JXADC[6]}] 228 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[6]}] 229 | ##Sch name = XA4_N 230 | #set_property PACKAGE_PIN N1 [get_ports {JXADC[7]}] 231 | #set_property IOSTANDARD LVCMOS33 [get_ports {JXADC[7]}] 232 | 233 | 234 | 235 | ##VGA Connector 236 | #set_property PACKAGE_PIN G19 [get_ports {vgaRed[0]}] 237 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[0]}] 238 | #set_property PACKAGE_PIN H19 [get_ports {vgaRed[1]}] 239 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[1]}] 240 | #set_property PACKAGE_PIN J19 [get_ports {vgaRed[2]}] 241 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[2]}] 242 | #set_property PACKAGE_PIN N19 [get_ports {vgaRed[3]}] 243 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[3]}] 244 | #set_property PACKAGE_PIN N18 [get_ports {vgaBlue[0]}] 245 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[0]}] 246 | #set_property PACKAGE_PIN L18 [get_ports {vgaBlue[1]}] 247 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[1]}] 248 | #set_property PACKAGE_PIN K18 [get_ports {vgaBlue[2]}] 249 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[2]}] 250 | #set_property PACKAGE_PIN J18 [get_ports {vgaBlue[3]}] 251 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[3]}] 252 | #set_property PACKAGE_PIN J17 [get_ports {vgaGreen[0]}] 253 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[0]}] 254 | #set_property PACKAGE_PIN H17 [get_ports {vgaGreen[1]}] 255 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[1]}] 256 | #set_property PACKAGE_PIN G17 [get_ports {vgaGreen[2]}] 257 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[2]}] 258 | #set_property PACKAGE_PIN D17 [get_ports {vgaGreen[3]}] 259 | #set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[3]}] 260 | #set_property PACKAGE_PIN P19 [get_ports Hsync] 261 | #set_property IOSTANDARD LVCMOS33 [get_ports Hsync] 262 | #set_property PACKAGE_PIN R19 [get_ports Vsync] 263 | #set_property IOSTANDARD LVCMOS33 [get_ports Vsync] 264 | 265 | 266 | ##USB-RS232 Interface 267 | #set_property PACKAGE_PIN B18 [get_ports RsRx] 268 | #set_property IOSTANDARD LVCMOS33 [get_ports RsRx] 269 | #set_property PACKAGE_PIN A18 [get_ports RsTx] 270 | #set_property IOSTANDARD LVCMOS33 [get_ports RsTx] 271 | 272 | 273 | ##USB HID (PS/2) 274 | #set_property PACKAGE_PIN C17 [get_ports PS2Clk] 275 | #set_property IOSTANDARD LVCMOS33 [get_ports PS2Clk] 276 | #set_property PULLUP true [get_ports PS2Clk] 277 | #set_property PACKAGE_PIN B17 [get_ports PS2Data] 278 | #set_property IOSTANDARD LVCMOS33 [get_ports PS2Data] 279 | #set_property PULLUP true [get_ports PS2Data] 280 | 281 | 282 | ##Quad SPI Flash 283 | ##Note that CCLK_0 cannot be placed in 7 series devices. You can access it using the 284 | ##STARTUPE2 primitive. 285 | #set_property PACKAGE_PIN D18 [get_ports {QspiDB[0]}] 286 | #set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[0]}] 287 | #set_property PACKAGE_PIN D19 [get_ports {QspiDB[1]}] 288 | #set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[1]}] 289 | #set_property PACKAGE_PIN G18 [get_ports {QspiDB[2]}] 290 | #set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[2]}] 291 | #set_property PACKAGE_PIN F18 [get_ports {QspiDB[3]}] 292 | #set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[3]}] 293 | #set_property PACKAGE_PIN K19 [get_ports QspiCSn] 294 | #set_property IOSTANDARD LVCMOS33 [get_ports QspiCSn] 295 | 296 | 297 | set_clock_groups -asynchronous -group sys_clk_pin 298 | -------------------------------------------------------------------------------- /tb/mmcme2_base_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * mmcme2_base_tb.v: Testbench for mmcme2_base.v 3 | * author: Till Mahlburg 4 | * year: 2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | `ifndef WAIT_INTERVAL 12 | `define WAIT_INTERVAL 1000 13 | `endif 14 | 15 | /* define all attributes as macros, so different combinations can be tested 16 | * more easily. 17 | * By default these are the given default values of the part. 18 | */ 19 | 20 | /* not implemented */ 21 | `ifndef BANDWIDTH 22 | `define BANDWIDTH "OPTIMIZED" 23 | `endif 24 | `ifndef CLKFBOUT_MULT_F 25 | `define CLKFBOUT_MULT_F 5.000 26 | `endif 27 | `ifndef CLKFBOUT_PHASE 28 | `define CLKFBOUT_PHASE 0.000 29 | `endif 30 | /* This deviates from the default values, because it is required to be set */ 31 | `ifndef CLKIN1_PERIOD 32 | `define CLKIN1_PERIOD 5.000 33 | `endif 34 | 35 | `ifndef CLKOUT0_DIVIDE_F 36 | `define CLKOUT0_DIVIDE_F 1.000 37 | `endif 38 | `ifndef CLKOUT1_DIVIDE 39 | `define CLKOUT1_DIVIDE 1 40 | `endif 41 | `ifndef CLKOUT2_DIVIDE 42 | `define CLKOUT2_DIVIDE 1 43 | `endif 44 | `ifndef CLKOUT2_DIVIDE 45 | `define CLKOUT2_DIVIDE 1 46 | `endif 47 | `ifndef CLKOUT3_DIVIDE 48 | `define CLKOUT3_DIVIDE 1 49 | `endif 50 | `ifndef CLKOUT4_DIVIDE 51 | `define CLKOUT4_DIVIDE 1 52 | `endif 53 | `ifndef CLKOUT5_DIVIDE 54 | `define CLKOUT5_DIVIDE 1 55 | `endif 56 | `ifndef CLKOUT6_DIVIDE 57 | `define CLKOUT6_DIVIDE 1 58 | `endif 59 | 60 | `ifndef CLKOUT0_DUTY_CYCLE 61 | `define CLKOUT0_DUTY_CYCLE 0.500 62 | `endif 63 | `ifndef CLKOUT1_DUTY_CYCLE 64 | `define CLKOUT1_DUTY_CYCLE 0.500 65 | `endif 66 | `ifndef CLKOUT2_DUTY_CYCLE 67 | `define CLKOUT2_DUTY_CYCLE 0.500 68 | `endif 69 | `ifndef CLKOUT3_DUTY_CYCLE 70 | `define CLKOUT3_DUTY_CYCLE 0.500 71 | `endif 72 | `ifndef CLKOUT4_DUTY_CYCLE 73 | `define CLKOUT4_DUTY_CYCLE 0.500 74 | `endif 75 | `ifndef CLKOUT5_DUTY_CYCLE 76 | `define CLKOUT5_DUTY_CYCLE 0.500 77 | `endif 78 | `ifndef CLKOUT6_DUTY_CYCLE 79 | `define CLKOUT6_DUTY_CYCLE 0.500 80 | `endif 81 | 82 | `ifndef CLKOUT0_PHASE 83 | `define CLKOUT0_PHASE 0.000 84 | `endif 85 | `ifndef CLKOUT1_PHASE 86 | `define CLKOUT1_PHASE 0.000 87 | `endif 88 | `ifndef CLKOUT2_PHASE 89 | `define CLKOUT2_PHASE 0.000 90 | `endif 91 | `ifndef CLKOUT3_PHASE 92 | `define CLKOUT3_PHASE 0.000 93 | `endif 94 | `ifndef CLKOUT4_PHASE 95 | `define CLKOUT4_PHASE 0.000 96 | `endif 97 | `ifndef CLKOUT5_PHASE 98 | `define CLKOUT5_PHASE 0.000 99 | `endif 100 | `ifndef CLKOUT6_PHASE 101 | `define CLKOUT6_PHASE 0.000 102 | `endif 103 | 104 | `ifndef CLKOUT4_CASCADE 105 | `define CLKOUT4_CASCADE "FALSE" 106 | `endif 107 | 108 | `ifndef DIVCLK_DIVIDE 109 | `define DIVCLK_DIVIDE 1 110 | `endif 111 | /* not implemented */ 112 | `ifndef REF_JITTER1 113 | `define REF_JITTER1 0.010 114 | `endif 115 | 116 | /* not implemented */ 117 | `ifndef STARTUP_WAIT 118 | `define STARTUP_WAIT "FALSE" 119 | `endif 120 | 121 | module MMCME2_BASE_tb(); 122 | wire CLKOUT[0:6]; 123 | wire CLKOUTB[0:3]; 124 | 125 | wire CLKFBOUT; 126 | wire CLKFBOUTB; 127 | wire LOCKED; 128 | 129 | reg CLKIN1; 130 | 131 | reg PWRDWN; 132 | reg RST; 133 | wire CLKFBIN; 134 | 135 | integer pass_count; 136 | integer fail_count; 137 | /* change according to the number of test cases */ 138 | localparam total = 31; 139 | 140 | reg reset; 141 | wire [31:0] period_1000[0:6]; 142 | wire [31:0] period_1000_fb; 143 | 144 | wire dcc_fail[0:6]; 145 | wire dcc_fail_fb; 146 | 147 | wire psc_fail[0:6]; 148 | wire psc_fail_fb; 149 | 150 | wire [31:0] CLKOUT_DIVIDE_1000[0:6]; 151 | assign CLKOUT_DIVIDE_1000[0] = `CLKOUT0_DIVIDE_F * 1000; 152 | assign CLKOUT_DIVIDE_1000[1] = `CLKOUT1_DIVIDE * 1000; 153 | assign CLKOUT_DIVIDE_1000[2] = `CLKOUT2_DIVIDE * 1000; 154 | assign CLKOUT_DIVIDE_1000[3] = `CLKOUT3_DIVIDE * 1000; 155 | assign CLKOUT_DIVIDE_1000[4] = `CLKOUT4_DIVIDE * 1000; 156 | assign CLKOUT_DIVIDE_1000[5] = `CLKOUT5_DIVIDE * 1000; 157 | assign CLKOUT_DIVIDE_1000[6] = `CLKOUT6_DIVIDE * 1000; 158 | 159 | wire [31:0] CLKOUT_DUTY_CYCLE_1000[0:6]; 160 | assign CLKOUT_DUTY_CYCLE_1000[0] = (`CLKOUT0_DUTY_CYCLE * 1000); 161 | assign CLKOUT_DUTY_CYCLE_1000[1] = (`CLKOUT1_DUTY_CYCLE * 1000); 162 | assign CLKOUT_DUTY_CYCLE_1000[2] = (`CLKOUT2_DUTY_CYCLE * 1000); 163 | assign CLKOUT_DUTY_CYCLE_1000[3] = (`CLKOUT3_DUTY_CYCLE * 1000); 164 | assign CLKOUT_DUTY_CYCLE_1000[4] = (`CLKOUT4_DUTY_CYCLE * 1000); 165 | assign CLKOUT_DUTY_CYCLE_1000[5] = (`CLKOUT5_DUTY_CYCLE * 1000); 166 | assign CLKOUT_DUTY_CYCLE_1000[6] = (`CLKOUT6_DUTY_CYCLE * 1000); 167 | 168 | wire [31:0] CLKOUT_PHASE_1000[0:6]; 169 | assign CLKOUT_PHASE_1000[0] = (`CLKOUT0_PHASE * 1000); 170 | assign CLKOUT_PHASE_1000[1] = (`CLKOUT1_PHASE * 1000); 171 | assign CLKOUT_PHASE_1000[2] = (`CLKOUT2_PHASE * 1000); 172 | assign CLKOUT_PHASE_1000[3] = (`CLKOUT3_PHASE * 1000); 173 | assign CLKOUT_PHASE_1000[4] = (`CLKOUT4_PHASE * 1000); 174 | assign CLKOUT_PHASE_1000[5] = (`CLKOUT5_PHASE * 1000); 175 | assign CLKOUT_PHASE_1000[6] = (`CLKOUT6_PHASE * 1000); 176 | 177 | /* instantiate PLLE2_BASE with default values for all the attributes */ 178 | MMCME2_BASE #( 179 | .BANDWIDTH(`BANDWIDTH), 180 | .CLKFBOUT_MULT_F(`CLKFBOUT_MULT_F), 181 | .CLKFBOUT_PHASE(`CLKFBOUT_PHASE), 182 | .CLKIN1_PERIOD(`CLKIN1_PERIOD), 183 | 184 | .CLKOUT0_DIVIDE_F(`CLKOUT0_DIVIDE_F), 185 | .CLKOUT1_DIVIDE(`CLKOUT1_DIVIDE), 186 | .CLKOUT2_DIVIDE(`CLKOUT2_DIVIDE), 187 | .CLKOUT3_DIVIDE(`CLKOUT3_DIVIDE), 188 | .CLKOUT4_DIVIDE(`CLKOUT4_DIVIDE), 189 | .CLKOUT5_DIVIDE(`CLKOUT5_DIVIDE), 190 | .CLKOUT6_DIVIDE(`CLKOUT6_DIVIDE), 191 | 192 | .CLKOUT0_DUTY_CYCLE(`CLKOUT0_DUTY_CYCLE), 193 | .CLKOUT1_DUTY_CYCLE(`CLKOUT1_DUTY_CYCLE), 194 | .CLKOUT2_DUTY_CYCLE(`CLKOUT2_DUTY_CYCLE), 195 | .CLKOUT3_DUTY_CYCLE(`CLKOUT3_DUTY_CYCLE), 196 | .CLKOUT4_DUTY_CYCLE(`CLKOUT4_DUTY_CYCLE), 197 | .CLKOUT5_DUTY_CYCLE(`CLKOUT5_DUTY_CYCLE), 198 | .CLKOUT6_DUTY_CYCLE(`CLKOUT6_DUTY_CYCLE), 199 | 200 | .CLKOUT0_PHASE(`CLKOUT0_PHASE), 201 | .CLKOUT1_PHASE(`CLKOUT1_PHASE), 202 | .CLKOUT2_PHASE(`CLKOUT2_PHASE), 203 | .CLKOUT3_PHASE(`CLKOUT3_PHASE), 204 | .CLKOUT4_PHASE(`CLKOUT4_PHASE), 205 | .CLKOUT5_PHASE(`CLKOUT5_PHASE), 206 | .CLKOUT6_PHASE(`CLKOUT6_PHASE), 207 | 208 | .CLKOUT4_CASCADE(`CLKOUT4_CASCADE), 209 | 210 | .DIVCLK_DIVIDE(`DIVCLK_DIVIDE), 211 | .REF_JITTER1(`REF_JITTER1), 212 | .STARTUP_WAIT(`STARTUP_WAIT)) 213 | dut ( 214 | .CLKOUT0(CLKOUT[0]), 215 | .CLKOUT1(CLKOUT[1]), 216 | .CLKOUT2(CLKOUT[2]), 217 | .CLKOUT3(CLKOUT[3]), 218 | .CLKOUT4(CLKOUT[4]), 219 | .CLKOUT5(CLKOUT[5]), 220 | .CLKOUT6(CLKOUT[6]), 221 | 222 | .CLKOUT0B(CLKOUTB[0]), 223 | .CLKOUT1B(CLKOUTB[1]), 224 | .CLKOUT2B(CLKOUTB[2]), 225 | .CLKOUT3B(CLKOUTB[3]), 226 | 227 | .CLKFBOUT(CLKFBOUT), 228 | .CLKFBOUTB(CLKFBOUTB), 229 | 230 | .LOCKED(LOCKED), 231 | 232 | .CLKIN1(CLKIN1), 233 | 234 | .PWRDWN(PWRDWN), 235 | .RST(RST), 236 | .CLKFBIN(CLKFBIN)); 237 | 238 | genvar i; 239 | generate 240 | for (i = 0; i <= 6; i = i + 1) begin : period_count 241 | period_count #( 242 | .RESOLUTION(0.01)) 243 | period_count ( 244 | .RST(reset), 245 | .clk(CLKOUT[i]), 246 | .period_length_1000(period_1000[i])); 247 | end 248 | 249 | for (i = 0; i <= 6; i = i + 1) begin : dcc 250 | if (i == 4 && `CLKOUT4_CASCADE == "TRUE") begin 251 | duty_cycle_check dcc ( 252 | .desired_duty_cycle_1000(CLKOUT_DUTY_CYCLE_1000[i]), 253 | .clk_period_1000(`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * ((CLKOUT_DIVIDE_1000[4] * CLKOUT_DIVIDE_1000[6]) / 1000.0)) / `CLKFBOUT_MULT_F)), 254 | .clk(CLKOUT[i]), 255 | .reset(reset), 256 | .LOCKED(LOCKED), 257 | .fail(dcc_fail[i])); 258 | end else if (i == 6 && `CLKOUT4_CASCADE == "TRUE") begin 259 | duty_cycle_check dcc ( 260 | .desired_duty_cycle_1000(CLKOUT_DUTY_CYCLE_1000[i]), 261 | .clk_period_1000(1000 * `CLKIN1_PERIOD * (`DIVCLK_DIVIDE / `CLKFBOUT_MULT_F)), 262 | .clk(CLKOUT[i]), 263 | .reset(reset), 264 | .LOCKED(LOCKED), 265 | .fail(dcc_fail[i])); 266 | end else begin 267 | duty_cycle_check dcc ( 268 | .desired_duty_cycle_1000(CLKOUT_DUTY_CYCLE_1000[i]), 269 | .clk_period_1000(`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * CLKOUT_DIVIDE_1000[i]) / `CLKFBOUT_MULT_F)), 270 | .clk(CLKOUT[i]), 271 | .reset(reset), 272 | .LOCKED(LOCKED), 273 | .fail(dcc_fail[i])); 274 | end 275 | end 276 | 277 | for (i = 0; i <= 6; i = i + 1) begin : psc 278 | phase_shift_check psc ( 279 | .desired_shift_1000(CLKOUT_PHASE_1000[i]), 280 | .clk_period_1000(`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * CLKOUT_DIVIDE_1000[i]) / `CLKFBOUT_MULT_F)), 281 | .clk_shifted(CLKOUT[i]), 282 | .clk(CLKFBOUT), 283 | .rst(RST), 284 | .LOCKED(LOCKED), 285 | .fail(psc_fail[i])); 286 | end 287 | endgenerate 288 | 289 | period_count period_count_fb ( 290 | .RST(reset), 291 | .clk(CLKFBOUT), 292 | .period_length_1000(period_1000_fb)); 293 | 294 | phase_shift_check pscfb ( 295 | .desired_shift_1000(`CLKFBOUT_PHASE * 1000), 296 | .clk_period_1000(`CLKIN1_PERIOD * (`DIVCLK_DIVIDE / `CLKFBOUT_MULT_F) * 1000), 297 | .clk_shifted(CLKFBOUT), 298 | .clk(CLKIN1), 299 | .rst(RST), 300 | .LOCKED(LOCKED), 301 | .fail(psc_fail_fb)); 302 | 303 | 304 | /* ------------ BEGIN TEST CASES ------------- */ 305 | /* default loop variable */ 306 | integer k; 307 | 308 | initial begin 309 | $dumpfile("mmcme2_base_tb.vcd"); 310 | $dumpvars(0, MMCME2_BASE_tb); 311 | 312 | pass_count = 0; 313 | fail_count = 0; 314 | reset = 0; 315 | 316 | CLKIN1 = 0; 317 | RST = 0; 318 | PWRDWN = 0; 319 | #10; 320 | reset = 1; 321 | RST = 1; 322 | #10; 323 | if ((CLKOUT[0] & CLKOUT[1] & CLKOUT[2] & CLKOUT[3] & CLKOUT[4] & CLKOUT[5] & CLKOUT[6] & ~CLKOUTB[0] & ~CLKOUTB[1] & ~CLKOUTB[2] & ~CLKOUTB[3] & CLKFBOUT & ~CLKFBOUTB & LOCKED) == 0) begin 324 | $display("PASSED: RST signal"); 325 | pass_count = pass_count + 1; 326 | end else begin 327 | $display("FAILED: RST signal"); 328 | fail_count = fail_count + 1; 329 | end 330 | reset = 0; 331 | RST = 0; 332 | /* Test for correct number of highs for the given parameters. 333 | * This is down for all six outputs and the feedback output. 334 | */ 335 | #`WAIT_INTERVAL; 336 | 337 | if (LOCKED === 1'b1) begin 338 | $display("PASSED: LOCKED"); 339 | pass_count = pass_count + 1; 340 | end else begin 341 | $display("FAILED: LOCKED"); 342 | fail_count = fail_count + 1; 343 | end 344 | 345 | /*------- INVERTED OUTPUTS --*/ 346 | for (k = 0; k <= 3; k = k + 1) begin 347 | if (CLKOUTB[k] == ~CLKOUT[k]) begin 348 | $display("PASSED: CLKOUT%0d inverted output", k); 349 | pass_count = pass_count + 1; 350 | end else begin 351 | $display("FAILED: CLKOUT%0d inverted output", k); 352 | fail_count = fail_count + 1; 353 | end 354 | end 355 | 356 | if (CLKFBOUTB == ~CLKFBOUT) begin 357 | $display("PASSED: CLKFBOUT inverted output"); 358 | pass_count = pass_count + 1; 359 | end else begin 360 | $display("FAILED: CLKFBOUT inverted output"); 361 | fail_count = fail_count + 1; 362 | end 363 | 364 | /*------- FREQUENCY ---------*/ 365 | for (k = 0; k <= 6; k = k + 1) begin 366 | if (k == 4 && `CLKOUT4_CASCADE == "TRUE") begin 367 | // Check for CLKOUT4 correctly, if CLKOUT4_CASCADE is set 368 | if ((period_1000[k] / 1000.0) == (`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * ((CLKOUT_DIVIDE_1000[k] * CLKOUT_DIVIDE_1000[k+2]) / (1000.0 * 1000.0)))/ `CLKFBOUT_MULT_F))) begin 369 | $display("PASSED: CLKOUT%0d frequency with CLKOUT4_CASCADE", k); 370 | pass_count = pass_count + 1; 371 | end else begin 372 | $display("FAILED: CLKOUT%0d frequency with CLKOUT4_CASCADE", k); 373 | fail_count = fail_count + 1; 374 | end 375 | end else if (k == 6 && `CLKOUT4_CASCADE == "TRUE") begin 376 | /* Don't check against the CLKOUT6 divider, because it is 377 | * used by CLKOUT4 */ 378 | if ((period_1000[k] / 1000.0) == `CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * 1.0) / `CLKFBOUT_MULT_F)) begin 379 | $display("PASSED: CLKOUT%0d frequeny with CLKOUT4_CASCADE", k); 380 | pass_count = pass_count + 1; 381 | end else begin 382 | $display("FAILED: CLKOUT%0d frequeny with CLKOUT4_CASCADE", k); 383 | fail_count = fail_count + 1; 384 | end 385 | end else begin 386 | if ((period_1000[k] / 1000.0) == `CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * (CLKOUT_DIVIDE_1000[k] / 1000.0) * 1.0) / `CLKFBOUT_MULT_F)) begin 387 | $display("PASSED: CLKOUT%0d frequency", k); 388 | pass_count = pass_count + 1; 389 | end else begin 390 | $display("FAILED: CLKOUT%0d frequency", k); 391 | fail_count = fail_count + 1; 392 | end 393 | end 394 | end 395 | 396 | if ((period_1000_fb / 1000.0) == (`CLKIN1_PERIOD * ((`DIVCLK_DIVIDE * 1.0) / `CLKFBOUT_MULT_F))) begin 397 | $display("PASSED: CLKFBOUT frequency"); 398 | pass_count = pass_count + 1; 399 | end else begin 400 | $display("FAILED: CLKFBOUT frequency"); 401 | fail_count = fail_count + 1; 402 | end 403 | 404 | 405 | /*------- DUTY CYCLE ---------*/ 406 | for (k = 0; k <= 6; k = k + 1) begin 407 | if (dcc_fail[k] !== 1'b1) begin 408 | $display("PASSED: CLKOUT%0d duty cycle", k); 409 | pass_count = pass_count + 1; 410 | end else begin 411 | $display("FAILED: CLKOUT%0d duty cycle", k); 412 | fail_count = fail_count + 1; 413 | end 414 | end 415 | 416 | /*------- PHASE SHIFT ---------*/ 417 | for (k = 0; k <= 6; k = k + 1) begin 418 | if (psc_fail[k] !== 1'b1) begin 419 | $display("PASSED: CLKOUT%0d phase shift", k); 420 | pass_count = pass_count + 1; 421 | end else begin 422 | $display("FAILED: CLKOUT%0d phase shift", k); 423 | fail_count = fail_count + 1; 424 | end 425 | end 426 | 427 | if (psc_fail_fb !== 1'b1) begin 428 | $display("PASSED: CLKOUTFB phase shift"); 429 | pass_count = pass_count + 1; 430 | end else begin 431 | $display("FAILED: CLKOUTFB phase shift"); 432 | fail_count = fail_count + 1; 433 | end 434 | 435 | 436 | PWRDWN = 1; 437 | #100; 438 | if ((CLKOUT[0] & CLKOUT[1] & CLKOUT[2] & CLKOUT[3] & CLKOUT[4] & CLKOUT[5] & CLKOUT[6] & CLKOUTB[0] & CLKOUTB[1] & CLKOUTB[2] & CLKOUTB[3] & CLKFBOUT & CLKFBOUTB & LOCKED) === 1'bx) begin 439 | $display("PASSED: PWRDWN"); 440 | pass_count = pass_count + 1; 441 | end else begin 442 | $display("FAILED: PWRDWN"); 443 | fail_count = fail_count + 1; 444 | end 445 | 446 | if ((pass_count + fail_count) == total) begin 447 | $display("PASSED: number of test cases"); 448 | pass_count = pass_count + 1; 449 | end else begin 450 | $display("FAILED: number of test cases"); 451 | fail_count = fail_count + 1; 452 | end 453 | 454 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 455 | 456 | $finish; 457 | end 458 | 459 | /* connect CLKFBIN with CLKFBOUT to use internal feedback */ 460 | assign CLKFBIN = CLKFBOUT; 461 | 462 | always #(`CLKIN1_PERIOD / 2) CLKIN1 = ~CLKIN1; 463 | endmodule 464 | -------------------------------------------------------------------------------- /tb/dyn_reconf_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * dyn_reconf_tb.v: Test bench for dyn_reconf.v 3 | * author: Till Mahlburg 4 | * year: 2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | `ifndef WAIT_INTERVAL 13 | `define WAIT_INTERVAL 1000 14 | `endif 15 | 16 | `ifndef CLK_PERIOD 17 | `define CLK_PERIOD 10 18 | `endif 19 | 20 | module dyn_reconf_tb (); 21 | reg RST; 22 | reg PWRDWN; 23 | 24 | reg [31:0] vco_period_1000; 25 | 26 | reg [6:0] DADDR; 27 | reg DCLK; 28 | reg DEN; 29 | reg DWE; 30 | reg [15:0] DI; 31 | 32 | wire [15:0] DO; 33 | output DRDY; 34 | 35 | wire [31:0] CLKOUT0_DIVIDE; 36 | wire [31:0] CLKOUT1_DIVIDE; 37 | wire [31:0] CLKOUT2_DIVIDE; 38 | wire [31:0] CLKOUT3_DIVIDE; 39 | wire [31:0] CLKOUT4_DIVIDE; 40 | wire [31:0] CLKOUT5_DIVIDE; 41 | wire [31:0] CLKOUT6_DIVIDE; 42 | 43 | wire [31:0] CLKOUT0_DUTY_CYCLE; 44 | wire [31:0] CLKOUT1_DUTY_CYCLE; 45 | wire [31:0] CLKOUT2_DUTY_CYCLE; 46 | wire [31:0] CLKOUT3_DUTY_CYCLE; 47 | wire [31:0] CLKOUT4_DUTY_CYCLE; 48 | wire [31:0] CLKOUT5_DUTY_CYCLE; 49 | wire [31:0] CLKOUT6_DUTY_CYCLE; 50 | 51 | wire [31:0] CLKOUT0_PHASE; 52 | wire [31:0] CLKOUT1_PHASE; 53 | wire [31:0] CLKOUT2_PHASE; 54 | wire [31:0] CLKOUT3_PHASE; 55 | wire [31:0] CLKOUT4_PHASE; 56 | wire [31:0] CLKOUT5_PHASE; 57 | wire [31:0] CLKOUT6_PHASE; 58 | 59 | wire [31:0] CLKFBOUT_MULT_F_1000; 60 | wire [31:0] CLKFBOUT_PHASE; 61 | 62 | wire [31:0] DIVCLK_DIVIDE; 63 | 64 | integer duty_cycle; 65 | 66 | integer pass_count; 67 | integer fail_count; 68 | 69 | /* adjust according to the number of test cases */ 70 | localparam total = 28; 71 | 72 | dyn_reconf dut( 73 | .RST(RST), 74 | .PWRDWN(PWRDWN), 75 | 76 | .vco_period_1000(vco_period_1000), 77 | 78 | .DADDR(DADDR), 79 | .DCLK(DCLK), 80 | .DEN(DEN), 81 | .DWE(DWE), 82 | .DI(DI), 83 | .DO(DO), 84 | .DRDY(DRDY), 85 | 86 | .CLKOUT0_DIVIDE(CLKOUT0_DIVIDE), 87 | .CLKOUT0_DUTY_CYCLE_1000(CLKOUT0_DUTY_CYCLE), 88 | .CLKOUT0_PHASE(CLKOUT0_PHASE), 89 | 90 | .CLKOUT1_DIVIDE(CLKOUT1_DIVIDE), 91 | .CLKOUT1_DUTY_CYCLE_1000(CLKOUT1_DUTY_CYCLE), 92 | .CLKOUT1_PHASE(CLKOUT1_PHASE), 93 | 94 | .CLKOUT2_DIVIDE(CLKOUT2_DIVIDE), 95 | .CLKOUT2_DUTY_CYCLE_1000(CLKOUT2_DUTY_CYCLE), 96 | .CLKOUT2_PHASE(CLKOUT2_PHASE), 97 | 98 | .CLKOUT3_DIVIDE(CLKOUT3_DIVIDE), 99 | .CLKOUT3_DUTY_CYCLE_1000(CLKOUT3_DUTY_CYCLE), 100 | .CLKOUT3_PHASE(CLKOUT3_PHASE), 101 | 102 | .CLKOUT4_DIVIDE(CLKOUT4_DIVIDE), 103 | .CLKOUT4_DUTY_CYCLE_1000(CLKOUT4_DUTY_CYCLE), 104 | .CLKOUT4_PHASE(CLKOUT4_PHASE), 105 | 106 | .CLKOUT5_DIVIDE(CLKOUT5_DIVIDE), 107 | .CLKOUT5_DUTY_CYCLE_1000(CLKOUT5_DUTY_CYCLE), 108 | .CLKOUT5_PHASE(CLKOUT5_PHASE), 109 | 110 | .CLKOUT6_DIVIDE(CLKOUT6_DIVIDE), 111 | .CLKOUT6_DUTY_CYCLE_1000(CLKOUT6_DUTY_CYCLE), 112 | .CLKOUT6_PHASE(CLKOUT6_PHASE), 113 | 114 | .CLKFBOUT_MULT_F_1000(CLKFBOUT_MULT_F_1000), 115 | .CLKFBOUT_PHASE(CLKFBOUT_PHASE), 116 | 117 | .DIVCLK_DIVIDE(DIVCLK_DIVIDE)); 118 | 119 | initial begin 120 | $dumpfile("dyn_reconf_tb.vcd"); 121 | $dumpvars(0, dyn_reconf_tb); 122 | 123 | vco_period_1000 = 32 * 1000; 124 | RST = 0; 125 | DCLK = 0; 126 | DADDR = 7'h00; 127 | DEN = 0; 128 | DWE = 0; 129 | DI = 15'h0000; 130 | 131 | pass_count = 0; 132 | fail_count = 0; 133 | 134 | #(`CLK_PERIOD * 2); 135 | RST = 1; 136 | #(`CLK_PERIOD * 2); 137 | 138 | /* TEST CASES */ 139 | 140 | if (DO == 15'h0000 && DRDY == 1'b1) begin 141 | $display("PASSED: RST"); 142 | pass_count = pass_count + 1; 143 | end else begin 144 | $display("FAILED: RST"); 145 | fail_count = fail_count + 1; 146 | end 147 | 148 | RST = 0; 149 | #(`CLK_PERIOD * 2); 150 | 151 | if (DRDY == 1'b1) begin 152 | $display("PASSED: release RST"); 153 | pass_count = pass_count + 1; 154 | end else begin 155 | $display("FAILED: release RST"); 156 | fail_count = fail_count + 1; 157 | end 158 | 159 | /* ClkReg1/2 for CLKOUT0 */ 160 | DADDR = 7'h08; 161 | DEN = 1'b1; 162 | DWE = 1'b1; 163 | /* PHASE MUX = 3 164 | * RESERVED = 0 165 | * HIGH TIME = 6 166 | * LOW TIME = 3 */ 167 | DI = 16'b011_0_000110_000011; 168 | 169 | #(`CLK_PERIOD * 2); 170 | if (DRDY == 1'b0) begin 171 | $display("PASSED: DRDY"); 172 | pass_count = pass_count + 1; 173 | end else begin 174 | $display("FAILED: DRDY"); 175 | fail_count = fail_count + 1; 176 | end 177 | 178 | DEN = 1'b0; 179 | DWE = 1'b0; 180 | 181 | #(`CLK_PERIOD * 2); 182 | 183 | if (DRDY == 1'b1) begin 184 | $display("PASSED: DEN and DWE"); 185 | pass_count = pass_count + 1; 186 | end else begin 187 | $display("FAILED: DEN and DWE"); 188 | fail_count = fail_count + 1; 189 | end 190 | 191 | DEN = 1'b1; 192 | #(`CLK_PERIOD * 2); 193 | 194 | if (DRDY == 1'b0 && DO == DI) begin 195 | $display("PASSED: DI and DO"); 196 | pass_count = pass_count + 1; 197 | end else begin 198 | $display("FAILED: DI and DO"); 199 | fail_count = fail_count + 1; 200 | end 201 | 202 | if (CLKOUT0_DIVIDE == 9) begin 203 | $display("PASSED: CLKOUT0 ClkReg1 DIVIDE calculation"); 204 | pass_count = pass_count + 1; 205 | end else begin 206 | $display("FAILED: CLKOUT0 ClkReg1 DIVIDE calculation"); 207 | fail_count = fail_count + 1; 208 | end 209 | 210 | duty_cycle = (6.0 * 1000) / (6.0 + 3.0); 211 | if (CLKOUT0_DUTY_CYCLE == duty_cycle) begin 212 | $display("PASSED: CLKOUT0 ClkReg1 DUTY_CYCLE calculation"); 213 | pass_count = pass_count + 1; 214 | end else begin 215 | $display("FAILED: CLKOUT0 ClkReg1 DUTY_CYCLE calculation"); 216 | fail_count = fail_count + 1; 217 | end 218 | 219 | if (CLKOUT0_PHASE == (((vco_period_1000 / 1000) / 8) * 3)) begin 220 | $display("PASSED: CLKOUT0 ClkReg1 PHASE calculation"); 221 | pass_count = pass_count + 1; 222 | end else begin 223 | $display("FAILED: CLKOUT0 ClkReg1 PHASE calculation"); 224 | fail_count = fail_count + 1; 225 | end 226 | DEN = 1'b0; 227 | #(`CLK_PERIOD * 2); 228 | DEN = 1'b1; 229 | DWE = 1'b1; 230 | DADDR = 7'h09; 231 | /* RESERVED = 0 232 | * FRAC = 000 233 | * FRAC_EN = 0 234 | * FRAC_WF_R = 0 235 | * MX = 2b'00 236 | * EDGE = 0 237 | * NO COUNT = 1 238 | * DELAY TIME = 3 */ 239 | DI = 16'b0_000_0_0_00_1_0_00011; 240 | 241 | #(`CLK_PERIOD * 2); 242 | DEN = 1'b0; 243 | DWE = 1'b0; 244 | #(`CLK_PERIOD * 2); 245 | 246 | if (CLKOUT0_DIVIDE == 1) begin 247 | $display("PASSED: CLKOUT0 ClkReg2 DIVIDE calculation"); 248 | pass_count = pass_count + 1; 249 | end else begin 250 | $display("FAILED: CLKOUT0 ClkReg2 DIVIDE calculation"); 251 | fail_count = fail_count + 1; 252 | end 253 | 254 | if ((CLKOUT0_DUTY_CYCLE / 1000.0) == 0.5) begin 255 | $display("PASSED: CLKOUT0 ClkReg2 DUTY_CYCLE calculation"); 256 | pass_count = pass_count + 1; 257 | end else begin 258 | $display("FAILED: CLKOUT0 ClkReg2 DUTY_CYCLE calculation"); 259 | fail_count = fail_count + 1; 260 | end 261 | 262 | if (CLKOUT0_PHASE == (((vco_period_1000 / 1000.0) / 8) * 3) + ((vco_period_1000 / 1000.0) * 3)) begin 263 | $display("PASSED: CLKOUT0 ClkReg2 PHASE calculation"); 264 | pass_count = pass_count + 1; 265 | end else begin 266 | $display("FAILED: CLKOUT0 ClkReg2 PHASE calculation"); 267 | fail_count = fail_count + 1; 268 | end 269 | 270 | /* CLKOUT1 */ 271 | 272 | DADDR = 7'h0A; 273 | DEN = 1'b1; 274 | DWE = 1'b1; 275 | /* PHASE MUX = 3 276 | * RESERVED = 0 277 | * HIGH TIME = 6 278 | * LOW TIME = 3 */ 279 | DI = 16'b011_0_000110_000011; 280 | #(`CLK_PERIOD * 2); 281 | 282 | DEN = 1'b0; 283 | DWE = 1'b0; 284 | #(`CLK_PERIOD * 2); 285 | 286 | DEN = 1'b1; 287 | #(`CLK_PERIOD * 2); 288 | 289 | if (CLKOUT1_DIVIDE == 9) begin 290 | $display("PASSED: CLKOUT1 ClkReg1 DIVIDE calculation"); 291 | pass_count = pass_count + 1; 292 | end else begin 293 | $display("FAILED: CLKOUT1 ClkReg1 DIVIDE calculation"); 294 | fail_count = fail_count + 1; 295 | end 296 | 297 | duty_cycle = (6.0 * 1000) / (6.0 + 3.0); 298 | if (CLKOUT1_DUTY_CYCLE == duty_cycle) begin 299 | $display("PASSED: CLKOUT1 ClkReg1 DUTY_CYCLE calculation"); 300 | pass_count = pass_count + 1; 301 | end else begin 302 | $display("FAILED: CLKOUT1 ClkReg1 DUTY_CYCLE calculation"); 303 | fail_count = fail_count + 1; 304 | end 305 | 306 | if (CLKOUT1_PHASE == (((vco_period_1000 / 1000) / 8) * 3)) begin 307 | $display("PASSED: CLKOUT1 ClkReg1 PHASE calculation"); 308 | pass_count = pass_count + 1; 309 | end else begin 310 | $display("FAILED: CLKOUT1 ClkReg1 PHASE calculation"); 311 | fail_count = fail_count + 1; 312 | end 313 | DEN = 1'b0; 314 | #(`CLK_PERIOD * 2); 315 | DEN = 1'b1; 316 | DWE = 1'b1; 317 | DADDR = 7'h0B; 318 | /* RESERVED = 0 319 | * FRAC = 000 320 | * FRAC_EN = 0 321 | * FRAC_WF_R = 0 322 | * MX = 2b'00 323 | * EDGE = 0 324 | * NO COUNT = 1 325 | * DELAY TIME = 3 */ 326 | DI = 16'b0_000_0_0_00_1_0_00011; 327 | 328 | #(`CLK_PERIOD * 2); 329 | DEN = 1'b0; 330 | DWE = 1'b0; 331 | #(`CLK_PERIOD * 2); 332 | 333 | if (CLKOUT1_DIVIDE == 1) begin 334 | $display("PASSED: CLKOUT1 ClkReg2 DIVIDE calculation"); 335 | pass_count = pass_count + 1; 336 | end else begin 337 | $display("FAILED: CLKOUT1 ClkReg2 DIVIDE calculation"); 338 | fail_count = fail_count + 1; 339 | end 340 | 341 | if ((CLKOUT1_DUTY_CYCLE / 1000.0) == 0.5) begin 342 | $display("PASSED: CLKOUT1 ClkReg2 DUTY_CYCLE calculation"); 343 | pass_count = pass_count + 1; 344 | end else begin 345 | $display("FAILED: CLKOUT1 ClkReg2 DUTY_CYCLE calculation"); 346 | fail_count = fail_count + 1; 347 | end 348 | 349 | if (CLKOUT1_PHASE == (((vco_period_1000 / 1000.0) / 8) * 3) + ((vco_period_1000 / 1000.0) * 3)) begin 350 | $display("PASSED: CLKOUT1 ClkReg2 PHASE calculation"); 351 | pass_count = pass_count + 1; 352 | end else begin 353 | $display("FAILED: CLKOUT1 ClkReg2 PHASE calculation"); 354 | fail_count = fail_count + 1; 355 | end 356 | 357 | /* CLKOUT5 */ 358 | 359 | DADDR = 7'h06; 360 | DEN = 1'b1; 361 | DWE = 1'b1; 362 | /* PHASE MUX = 3 363 | * RESERVED = 0 364 | * HIGH TIME = 6 365 | * LOW TIME = 3 */ 366 | DI = 16'b011_0_000110_000011; 367 | #(`CLK_PERIOD * 2); 368 | 369 | DEN = 1'b0; 370 | DWE = 1'b0; 371 | #(`CLK_PERIOD * 2); 372 | 373 | DEN = 1'b1; 374 | #(`CLK_PERIOD * 2); 375 | 376 | if (CLKOUT5_DIVIDE == 9) begin 377 | $display("PASSED: CLKOUT5 ClkReg1 DIVIDE calculation"); 378 | pass_count = pass_count + 1; 379 | end else begin 380 | $display("FAILED: CLKOUT5 ClkReg1 DIVIDE calculation"); 381 | fail_count = fail_count + 1; 382 | end 383 | 384 | duty_cycle = (6.0 * 1000) / (6.0 + 3.0); 385 | if (CLKOUT5_DUTY_CYCLE == duty_cycle) begin 386 | $display("PASSED: CLKOUT5 ClkReg1 DUTY_CYCLE calculation"); 387 | pass_count = pass_count + 1; 388 | end else begin 389 | $display("FAILED: CLKOUT5 ClkReg1 DUTY_CYCLE calculation"); 390 | fail_count = fail_count + 1; 391 | end 392 | 393 | if (CLKOUT5_PHASE == (((vco_period_1000 / 1000) / 8) * 3)) begin 394 | $display("PASSED: CLKOUT5 ClkReg1 PHASE calculation"); 395 | pass_count = pass_count + 1; 396 | end else begin 397 | $display("FAILED: CLKOUT5 ClkReg1 PHASE calculation"); 398 | fail_count = fail_count + 1; 399 | end 400 | DEN = 1'b0; 401 | #(`CLK_PERIOD * 2); 402 | DEN = 1'b1; 403 | DWE = 1'b1; 404 | DADDR = 7'h07; 405 | /* RESERVED = 00 406 | * PHASE_MUX_F_CLKOUT0 = 000 407 | * FRAC_WF_F_CLKOUT0 = 0 408 | * MX = 2b'00 409 | * EDGE = 0 410 | * NO COUNT = 1 411 | * DELAY TIME = 3 */ 412 | DI = 16'b00_000_0_00_1_0_00011; 413 | 414 | #(`CLK_PERIOD * 2); 415 | DEN = 1'b0; 416 | DWE = 1'b0; 417 | #(`CLK_PERIOD * 2); 418 | 419 | if (CLKOUT5_DIVIDE == 1) begin 420 | $display("PASSED: CLKOUT5 ClkReg2 DIVIDE calculation"); 421 | pass_count = pass_count + 1; 422 | end else begin 423 | $display("FAILED: CLKOUT5 ClkReg2 DIVIDE calculation"); 424 | fail_count = fail_count + 1; 425 | end 426 | 427 | if ((CLKOUT5_DUTY_CYCLE / 1000.0) == 0.5) begin 428 | $display("PASSED: CLKOUT5 ClkReg2 DUTY_CYCLE calculation"); 429 | pass_count = pass_count + 1; 430 | end else begin 431 | $display("FAILED: CLKOUT5 ClkReg2 DUTY_CYCLE calculation"); 432 | fail_count = fail_count + 1; 433 | end 434 | 435 | if (CLKOUT5_PHASE == (((vco_period_1000 / 1000.0) / 8) * 3) + ((vco_period_1000 / 1000.0) * 3)) begin 436 | $display("PASSED: CLKOUT5 ClkReg2 PHASE calculation"); 437 | pass_count = pass_count + 1; 438 | end else begin 439 | $display("FAILED: CLKOUT5 ClkReg2 PHASE calculation"); 440 | fail_count = fail_count + 1; 441 | end 442 | 443 | /* CLKFBOUT */ 444 | DADDR = 7'h14; 445 | DEN = 1'b1; 446 | DWE = 1'b1; 447 | /* PHASE MUX = 3 448 | * RESERVED = 0 449 | * HIGH TIME = 6 450 | * LOW TIME = 3 */ 451 | DI = 16'b011_0_000110_000011; 452 | #(`CLK_PERIOD * 2); 453 | 454 | DEN = 1'b0; 455 | DWE = 1'b0; 456 | #(`CLK_PERIOD * 2); 457 | 458 | DEN = 1'b1; 459 | #(`CLK_PERIOD * 2); 460 | 461 | if (CLKFBOUT_MULT_F_1000 == 9000) begin 462 | $display("PASSED: CLKFBOUT ClkReg1 DIVIDE calculation"); 463 | pass_count = pass_count + 1; 464 | end else begin 465 | $display("FAILED: CLKFBOUT ClkReg1 DIVIDE calculation"); 466 | fail_count = fail_count + 1; 467 | end 468 | 469 | if (CLKFBOUT_PHASE == (((vco_period_1000 / 1000) / 8) * 3)) begin 470 | $display("PASSED: CLKFBOUT ClkReg1 PHASE calculation"); 471 | pass_count = pass_count + 1; 472 | end else begin 473 | $display("FAILED: CLKFBOUT ClkReg1 PHASE calculation"); 474 | fail_count = fail_count + 1; 475 | end 476 | DEN = 1'b0; 477 | #(`CLK_PERIOD * 2); 478 | DEN = 1'b1; 479 | DWE = 1'b1; 480 | DADDR = 7'h15; 481 | /* RESERVED = 0 482 | * FRAC = 000 483 | * FRAC_EN = 0 484 | * FRAC_WF_R = 0 485 | * MX = 2b'00 486 | * EDGE = 0 487 | * NO COUNT = 1 488 | * DELAY TIME = 3 */ 489 | DI = 16'b0_000_0_0_00_1_0_00011; 490 | 491 | #(`CLK_PERIOD * 2); 492 | DEN = 1'b0; 493 | DWE = 1'b0; 494 | #(`CLK_PERIOD * 2); 495 | 496 | if (CLKFBOUT_MULT_F_1000 == 1000) begin 497 | $display("PASSED: CLKFBOUT ClkReg2 DIVIDE calculation"); 498 | pass_count = pass_count + 1; 499 | end else begin 500 | $display("FAILED: CLKFBOUT ClkReg2 DIVIDE calculation"); 501 | fail_count = fail_count + 1; 502 | end 503 | 504 | if (CLKFBOUT_PHASE == (((vco_period_1000 / 1000.0) / 8) * 3) + ((vco_period_1000 / 1000.0) * 3)) begin 505 | $display("PASSED: CLKFBOUT ClkReg2 PHASE calculation"); 506 | pass_count = pass_count + 1; 507 | end else begin 508 | $display("FAILED: CLKFBOUT ClkReg2 PHASE calculation"); 509 | fail_count = fail_count + 1; 510 | end 511 | 512 | /* DIVCLK_DIVIDE */ 513 | DADDR = 7'h16; 514 | DEN = 1'b1; 515 | DWE = 1'b1; 516 | /* RESERVED = 0 517 | * EDGE = 0 518 | * NO COUNT = 0 519 | * HIGH TIME = 3 520 | * LOW TIME = 3 */ 521 | DI = 16'b00_0_0_000011_000011; 522 | #(`CLK_PERIOD * 2); 523 | 524 | DEN = 1'b0; 525 | DWE = 1'b0; 526 | #(`CLK_PERIOD * 2); 527 | 528 | DEN = 1'b1; 529 | #(`CLK_PERIOD * 2); 530 | 531 | if (DIVCLK_DIVIDE == 6) begin 532 | $display("PASSED: ClkReg1 DIVIDE calculation"); 533 | pass_count = pass_count + 1; 534 | end else begin 535 | $display("FAILED: ClkReg1 DIVIDE calculation"); 536 | fail_count = fail_count + 1; 537 | end 538 | 539 | 540 | if ((pass_count + fail_count) == total) begin 541 | $display("PASSED: number of test cases"); 542 | pass_count = pass_count + 1; 543 | end else begin 544 | $display("FAILED: number of test cases"); 545 | fail_count = fail_count + 1; 546 | end 547 | 548 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 549 | $finish; 550 | end 551 | 552 | always #`CLK_PERIOD DCLK <= ~DCLK; 553 | endmodule 554 | -------------------------------------------------------------------------------- /tb/plle2_adv_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * plle2_adv_tb.v: Testbench for plle2_adv.v 3 | * author: Till Mahlburg 4 | * year: 2019-2020 5 | * organization: Universität Leipzig 6 | * license: ISC 7 | * 8 | */ 9 | 10 | `timescale 1 ns / 1 ps 11 | 12 | /* define all attributes as macros, so different combinations can be tested 13 | * more easily. 14 | * By default these are the given default values of the part. 15 | */ 16 | 17 | /* not implemented */ 18 | `ifndef WAIT_INTERVAL 19 | `define WAIT_INTERVAL 1000 20 | `endif 21 | `ifndef BANDWIDTH 22 | `define BANDWIDTH "OPTIMIZED" 23 | `endif 24 | `ifndef CLKFBOUT_MULT 25 | `define CLKFBOUT_MULT 5 26 | `endif 27 | `ifndef CLKFBOUT_PHASE 28 | `define CLKFBOUT_PHASE 0.000 29 | `endif 30 | /* This deviates from the default values, because it is required to be set */ 31 | `ifndef CLKIN1_PERIOD 32 | `define CLKIN1_PERIOD 5.000 33 | `endif 34 | `ifndef CLKIN2_PERIOD 35 | `define CLKIN2_PERIOD 4.000 36 | `endif 37 | 38 | `ifndef CLKOUT0_DIVIDE 39 | `define CLKOUT0_DIVIDE 1 40 | `endif 41 | `ifndef CLKOUT1_DIVIDE 42 | `define CLKOUT1_DIVIDE 1 43 | `endif 44 | `ifndef CLKOUT2_DIVIDE 45 | `define CLKOUT2_DIVIDE 1 46 | `endif 47 | `ifndef CLKOUT2_DIVIDE 48 | `define CLKOUT2_DIVIDE 1 49 | `endif 50 | `ifndef CLKOUT3_DIVIDE 51 | `define CLKOUT3_DIVIDE 1 52 | `endif 53 | `ifndef CLKOUT4_DIVIDE 54 | `define CLKOUT4_DIVIDE 1 55 | `endif 56 | `ifndef CLKOUT5_DIVIDE 57 | `define CLKOUT5_DIVIDE 1 58 | `endif 59 | 60 | `ifndef CLKOUT0_DUTY_CYCLE 61 | `define CLKOUT0_DUTY_CYCLE 0.500 62 | `endif 63 | `ifndef CLKOUT1_DUTY_CYCLE 64 | `define CLKOUT1_DUTY_CYCLE 0.500 65 | `endif 66 | `ifndef CLKOUT2_DUTY_CYCLE 67 | `define CLKOUT2_DUTY_CYCLE 0.500 68 | `endif 69 | `ifndef CLKOUT3_DUTY_CYCLE 70 | `define CLKOUT3_DUTY_CYCLE 0.500 71 | `endif 72 | `ifndef CLKOUT4_DUTY_CYCLE 73 | `define CLKOUT4_DUTY_CYCLE 0.500 74 | `endif 75 | `ifndef CLKOUT5_DUTY_CYCLE 76 | `define CLKOUT5_DUTY_CYCLE 0.500 77 | `endif 78 | 79 | `ifndef CLKOUT0_PHASE 80 | `define CLKOUT0_PHASE 0.000 81 | `endif 82 | `ifndef CLKOUT1_PHASE 83 | `define CLKOUT1_PHASE 0.000 84 | `endif 85 | `ifndef CLKOUT2_PHASE 86 | `define CLKOUT2_PHASE 0.000 87 | `endif 88 | `ifndef CLKOUT3_PHASE 89 | `define CLKOUT3_PHASE 0.000 90 | `endif 91 | `ifndef CLKOUT4_PHASE 92 | `define CLKOUT4_PHASE 0.000 93 | `endif 94 | `ifndef CLKOUT5_PHASE 95 | `define CLKOUT5_PHASE 0.000 96 | `endif 97 | 98 | `ifndef DIVCLK_DIVIDE 99 | `define DIVCLK_DIVIDE 1 100 | `endif 101 | /* not implemented */ 102 | `ifndef REF_JITTER1 103 | `define REF_JITTER1 0.010 104 | `endif 105 | /* not inplemented */ 106 | `ifndef REF_JITTER2 107 | `define REF_JITTER2 0.010 108 | `endif 109 | 110 | /* not implemented */ 111 | `ifndef STARTUP_WAIT 112 | `define STARTUP_WAIT "FALSE" 113 | `endif 114 | 115 | /* not implemented */ 116 | `ifndef COMPENSATION 117 | `define COMPENSATION "ZHOLD" 118 | `endif 119 | 120 | 121 | /* set two DADDR and two DI to test the dynamic reconfiguration */ 122 | `ifndef DADDR1 123 | `define DADDR1 7'h08 124 | `endif 125 | 126 | `ifndef DADDR2 127 | `define DADDR2 7'h09 128 | `endif 129 | 130 | `ifndef DI1 131 | `define DI1 16'b011_0_000110_000011 132 | `endif 133 | 134 | `ifndef DI2 135 | `define DI2 16'b0_000_0_0_00_0_0_00011 136 | `endif 137 | 138 | `ifndef DCLK_PERIOD 139 | `define DCLK_PERIOD 2 140 | `endif 141 | 142 | 143 | module PLLE2_ADV_tb(); 144 | wire CLKOUT[0:5]; 145 | 146 | wire CLKFBOUT; 147 | wire LOCKED; 148 | 149 | reg CLKIN1; 150 | reg CLKIN2; 151 | reg CLKINSEL; 152 | 153 | reg PWRDWN; 154 | reg RST; 155 | wire CLKFBIN; 156 | 157 | reg [6:0] DADDR; 158 | reg DCLK; 159 | reg DEN; 160 | reg DWE; 161 | reg [15:0] DI; 162 | 163 | wire [15:0] DO; 164 | wire DRDY; 165 | 166 | integer pass_count; 167 | integer fail_count; 168 | /* change according to the number of test cases */ 169 | localparam total = 47; 170 | 171 | reg reset; 172 | wire [31:0] period_1000[0:5]; 173 | wire [31:0] period_1000_fb; 174 | 175 | wire dcc_fail[0:5]; 176 | wire dcc_fail_fb; 177 | 178 | wire psc_fail[0:5]; 179 | wire psc_fail_fb; 180 | 181 | integer CLKOUT_DIVIDE[0:5]; 182 | integer CLKOUT_DUTY_CYCLE_1000[0:5]; 183 | integer CLKOUT_PHASE_1000[0:5]; 184 | 185 | integer CLKFBOUT_MULT; 186 | integer CLKFBOUT_PHASE; 187 | integer DIVCLK_DIVIDE; 188 | 189 | integer CLKIN_PERIOD_1000; 190 | 191 | /* instantiate PLLE2_ADV with default values for all the attributes */ 192 | PLLE2_ADV #( 193 | .BANDWIDTH(`BANDWIDTH), 194 | .CLKFBOUT_MULT(`CLKFBOUT_MULT), 195 | .CLKFBOUT_PHASE(`CLKFBOUT_PHASE), 196 | .CLKIN1_PERIOD(`CLKIN1_PERIOD), 197 | .CLKIN2_PERIOD(`CLKIN2_PERIOD), 198 | 199 | .CLKOUT0_DIVIDE(`CLKOUT0_DIVIDE), 200 | .CLKOUT1_DIVIDE(`CLKOUT1_DIVIDE), 201 | .CLKOUT2_DIVIDE(`CLKOUT2_DIVIDE), 202 | .CLKOUT3_DIVIDE(`CLKOUT3_DIVIDE), 203 | .CLKOUT4_DIVIDE(`CLKOUT4_DIVIDE), 204 | .CLKOUT5_DIVIDE(`CLKOUT5_DIVIDE), 205 | 206 | .CLKOUT0_DUTY_CYCLE(`CLKOUT0_DUTY_CYCLE), 207 | .CLKOUT1_DUTY_CYCLE(`CLKOUT1_DUTY_CYCLE), 208 | .CLKOUT2_DUTY_CYCLE(`CLKOUT2_DUTY_CYCLE), 209 | .CLKOUT3_DUTY_CYCLE(`CLKOUT3_DUTY_CYCLE), 210 | .CLKOUT4_DUTY_CYCLE(`CLKOUT4_DUTY_CYCLE), 211 | .CLKOUT5_DUTY_CYCLE(`CLKOUT5_DUTY_CYCLE), 212 | 213 | .CLKOUT0_PHASE(`CLKOUT0_PHASE), 214 | .CLKOUT1_PHASE(`CLKOUT1_PHASE), 215 | .CLKOUT2_PHASE(`CLKOUT2_PHASE), 216 | .CLKOUT3_PHASE(`CLKOUT3_PHASE), 217 | .CLKOUT4_PHASE(`CLKOUT4_PHASE), 218 | .CLKOUT5_PHASE(`CLKOUT5_PHASE), 219 | 220 | .DIVCLK_DIVIDE(`DIVCLK_DIVIDE), 221 | .REF_JITTER1(`REF_JITTER1), 222 | .REF_JITTER2(`REF_JITTER2), 223 | .STARTUP_WAIT(`STARTUP_WAIT), 224 | .COMPENSATION(`COMPENSATION)) 225 | dut ( 226 | .CLKOUT0(CLKOUT[0]), 227 | .CLKOUT1(CLKOUT[1]), 228 | .CLKOUT2(CLKOUT[2]), 229 | .CLKOUT3(CLKOUT[3]), 230 | .CLKOUT4(CLKOUT[4]), 231 | .CLKOUT5(CLKOUT[5]), 232 | 233 | .CLKFBOUT(CLKFBOUT), 234 | .LOCKED(LOCKED), 235 | 236 | .CLKIN1(CLKIN1), 237 | .CLKIN2(CLKIN2), 238 | .CLKINSEL(CLKINSEL), 239 | 240 | .PWRDWN(PWRDWN), 241 | .RST(RST), 242 | .CLKFBIN(CLKFBIN), 243 | 244 | .DADDR(DADDR), 245 | .DCLK(DCLK), 246 | .DEN(DEN), 247 | .DWE(DWE), 248 | .DI(DI), 249 | 250 | .DO(DO), 251 | .DRDY(DRDY) 252 | ); 253 | 254 | genvar i; 255 | generate 256 | for (i = 0; i <= 5; i = i + 1) begin : period_count 257 | period_count period_count ( 258 | .RST(reset), 259 | .PWRDWN(1'b0), 260 | .clk(CLKOUT[i]), 261 | .period_length_1000(period_1000[i])); 262 | end 263 | 264 | for (i = 0; i <= 5; i = i + 1) begin : dcc 265 | duty_cycle_check dcc ( 266 | .desired_duty_cycle_1000(CLKOUT_DUTY_CYCLE_1000[i]), 267 | .clk_period_1000(((CLKIN_PERIOD_1000 / 1000.0) * ((DIVCLK_DIVIDE * CLKOUT_DIVIDE[i]) / CLKFBOUT_MULT)) * 1000), 268 | .clk(CLKOUT[i]), 269 | .reset(reset), 270 | .LOCKED(LOCKED), 271 | .fail(dcc_fail[i])); 272 | end 273 | 274 | for (i = 0; i <= 5; i = i + 1) begin : psc 275 | phase_shift_check psc ( 276 | .desired_shift_1000(CLKOUT_PHASE_1000[i]), 277 | .clk_period_1000(1000 * (CLKIN_PERIOD_1000 / 1000.0) * ((DIVCLK_DIVIDE * CLKOUT_DIVIDE[i]) / CLKFBOUT_MULT)), 278 | .clk_shifted(CLKOUT[i]), 279 | .clk(CLKFBOUT), 280 | .rst(RST), 281 | .LOCKED(LOCKED), 282 | .fail(psc_fail[i])); 283 | end 284 | endgenerate 285 | 286 | period_count period_count_fb ( 287 | .RST(reset), 288 | .clk(CLKFBOUT), 289 | .period_length_1000(period_1000_fb)); 290 | 291 | phase_shift_check pscfb ( 292 | .desired_shift_1000(CLKFBOUT_PHASE * 1000), 293 | .clk_period_1000((CLKIN_PERIOD_1000 / 1000.0) * (DIVCLK_DIVIDE / CLKFBOUT_MULT) * 1000), 294 | .clk_shifted(CLKFBOUT), 295 | .clk(CLKIN1), 296 | .rst(RST), 297 | .LOCKED(LOCKED), 298 | .fail(psc_fail_fb)); 299 | 300 | /* ------------ BEGIN TEST CASES ------------- */ 301 | /* default loop variable */ 302 | integer k; 303 | 304 | initial begin 305 | $dumpfile("plle2_adv_tb.vcd"); 306 | $dumpvars(0, PLLE2_ADV_tb); 307 | 308 | pass_count = 0; 309 | fail_count = 0; 310 | reset = 0; 311 | 312 | CLKINSEL = 0; 313 | CLKIN_PERIOD_1000 = `CLKIN2_PERIOD * 1000; 314 | CLKIN1 = 0; 315 | CLKIN2 = 0; 316 | RST = 0; 317 | PWRDWN = 0; 318 | 319 | /* set up initial values */ 320 | CLKOUT_DIVIDE[0] = `CLKOUT0_DIVIDE; 321 | CLKOUT_DIVIDE[1] = `CLKOUT1_DIVIDE; 322 | CLKOUT_DIVIDE[2] = `CLKOUT2_DIVIDE; 323 | CLKOUT_DIVIDE[3] = `CLKOUT3_DIVIDE; 324 | CLKOUT_DIVIDE[4] = `CLKOUT4_DIVIDE; 325 | CLKOUT_DIVIDE[5] = `CLKOUT5_DIVIDE; 326 | 327 | CLKOUT_DUTY_CYCLE_1000[0] = (`CLKOUT0_DUTY_CYCLE * 1000); 328 | CLKOUT_DUTY_CYCLE_1000[1] = (`CLKOUT1_DUTY_CYCLE * 1000); 329 | CLKOUT_DUTY_CYCLE_1000[2] = (`CLKOUT2_DUTY_CYCLE * 1000); 330 | CLKOUT_DUTY_CYCLE_1000[3] = (`CLKOUT3_DUTY_CYCLE * 1000); 331 | CLKOUT_DUTY_CYCLE_1000[4] = (`CLKOUT4_DUTY_CYCLE * 1000); 332 | CLKOUT_DUTY_CYCLE_1000[5] = (`CLKOUT5_DUTY_CYCLE * 1000); 333 | 334 | CLKOUT_PHASE_1000[0] = (`CLKOUT0_PHASE * 1000); 335 | CLKOUT_PHASE_1000[1] = (`CLKOUT1_PHASE * 1000); 336 | CLKOUT_PHASE_1000[2] = (`CLKOUT2_PHASE * 1000); 337 | CLKOUT_PHASE_1000[3] = (`CLKOUT3_PHASE * 1000); 338 | CLKOUT_PHASE_1000[4] = (`CLKOUT4_PHASE * 1000); 339 | CLKOUT_PHASE_1000[5] = (`CLKOUT5_PHASE * 1000); 340 | 341 | CLKFBOUT_MULT = `CLKFBOUT_MULT; 342 | CLKFBOUT_PHASE = `CLKFBOUT_PHASE; 343 | DIVCLK_DIVIDE = `DIVCLK_DIVIDE; 344 | 345 | DADDR = 7'h00; 346 | DI = 16'h0000; 347 | DEN = 1'b0; 348 | DWE = 1'b0; 349 | DCLK = 1'b0; 350 | 351 | #10; 352 | reset = 1; 353 | RST = 1; 354 | #10; 355 | if ((CLKOUT[0] & CLKOUT[1] & CLKOUT[2] & CLKOUT[3] & CLKOUT[4] & CLKOUT[5] & CLKFBOUT & LOCKED) == 0) begin 356 | $display("PASSED: RST signal"); 357 | pass_count = pass_count + 1; 358 | end else begin 359 | $display("FAILED: RST signal"); 360 | fail_count = fail_count + 1; 361 | end 362 | reset = 0; 363 | RST = 0; 364 | #`WAIT_INTERVAL; 365 | 366 | if (LOCKED === 1'b1) begin 367 | $display("PASSED: LOCKED"); 368 | pass_count = pass_count + 1; 369 | end else begin 370 | $display("FAILED: LOCKED"); 371 | fail_count = fail_count + 1; 372 | end 373 | 374 | /*------ CLKIN SELECTION --- */ 375 | if ((period_1000_fb / 1000.0) == (`CLKIN2_PERIOD * ((`DIVCLK_DIVIDE * 1.0) / `CLKFBOUT_MULT))) begin 376 | $display("PASSED: CLKIN2 selection"); 377 | pass_count = pass_count + 1; 378 | end else begin 379 | $display("FAILED: CLKIN2 selection"); 380 | fail_count = fail_count + 1; 381 | end 382 | 383 | /* switch clock back to 1 */ 384 | CLKINSEL = 1; 385 | CLKIN_PERIOD_1000 = `CLKIN1_PERIOD * 1000; 386 | reset = 1; 387 | #(`WAIT_INTERVAL / 10); 388 | reset = 0; 389 | #`WAIT_INTERVAL; 390 | 391 | /*------- FREQUENCY ---------*/ 392 | for (k = 0; k <= 5; k = k + 1) begin 393 | if ((period_1000[k]) == (1000 * `CLKIN1_PERIOD * ((DIVCLK_DIVIDE * CLKOUT_DIVIDE[k] * 1.0) / CLKFBOUT_MULT))) begin 394 | $display("PASSED: CLKOUT%0d frequency", k); 395 | pass_count = pass_count + 1; 396 | end else begin 397 | $display("FAILED: CLKOUT%0d frequency", k); 398 | fail_count = fail_count + 1; 399 | end 400 | end 401 | 402 | if ((period_1000_fb) == (1000 * `CLKIN1_PERIOD * ((DIVCLK_DIVIDE * 1.0) / CLKFBOUT_MULT))) begin 403 | $display("PASSED: CLKFBOUT frequency"); 404 | pass_count = pass_count + 1; 405 | end else begin 406 | $display("FAILED: CLKFBOUT frequency"); 407 | fail_count = fail_count + 1; 408 | end 409 | 410 | 411 | /*------- DUTY CYCLE ---------*/ 412 | for (k = 0; k <= 5; k = k + 1) begin 413 | if (dcc_fail[k] !== 1'b1) begin 414 | $display("PASSED: CLKOUT%0d duty cycle", k); 415 | pass_count = pass_count + 1; 416 | end else begin 417 | $display("FAILED: CLKOUT%0d duty cycle", k); 418 | fail_count = fail_count + 1; 419 | end 420 | end 421 | 422 | /*------- PHASE SHIFT ---------*/ 423 | for (k = 0; k <= 5; k = k + 1) begin 424 | if (psc_fail[k] !== 1'b1) begin 425 | $display("PASSED: CLKOUT%0d phase shift", k); 426 | pass_count = pass_count + 1; 427 | end else begin 428 | $display("FAILED: CLKOUT%0d phase shift", k); 429 | fail_count = fail_count + 1; 430 | end 431 | end 432 | 433 | if (psc_fail_fb !== 1'b1) begin 434 | $display("PASSED: CLKFBOUT phase shift"); 435 | pass_count = pass_count + 1; 436 | end else begin 437 | $display("FAILED: CLKFBOUT phase shift"); 438 | fail_count = fail_count + 1; 439 | end 440 | 441 | /*------- DYNAMIC RECONFIGURATION ---------*/ 442 | if (DRDY == 1'b1) begin 443 | $display("PASSED: DRDY high"); 444 | pass_count = pass_count + 1; 445 | end else begin 446 | $display("FAILED: DRDY high"); 447 | fail_count = fail_count + 1; 448 | end 449 | 450 | DADDR = `DADDR1; 451 | DI = `DI1; 452 | DEN = 1'b1; 453 | DWE = 1'b1; 454 | #`DCLK_PERIOD; 455 | 456 | if (DRDY == 1'b0) begin 457 | $display("PASSED: DRDY low"); 458 | pass_count = pass_count + 1; 459 | end else begin 460 | $display("FAILED: DRDY low"); 461 | fail_count = fail_count + 1; 462 | end 463 | 464 | DWE = 1'b0; 465 | DEN = 1'b0; 466 | #(`DCLK_PERIOD * 2); 467 | 468 | DEN = 1'b1; 469 | #(`DCLK_PERIOD * 2); 470 | 471 | if (DO == DI) begin 472 | $display("PASSED: DO"); 473 | pass_count = pass_count + 1; 474 | end else begin 475 | $display("FAILED: DO"); 476 | fail_count = fail_count + 1; 477 | end 478 | 479 | DEN = 1'b0; 480 | #(`DCLK_PERIOD * 2); 481 | DADDR = `DADDR2; 482 | DI = `DI2; 483 | DEN = 1'b1; 484 | DWE = 1'b1; 485 | #(`DCLK_PERIOD * 2); 486 | DEN = 1'b0; 487 | DWE = 1'b0; 488 | reset = 1; 489 | #`WAIT_INTERVAL; 490 | reset = 0; 491 | #`WAIT_INTERVAL; 492 | 493 | /*------- FREQUENCY ---------*/ 494 | for (k = 0; k <= 5; k = k + 1) begin 495 | if ((period_1000[k]) == (1000 * `CLKIN1_PERIOD * ((DIVCLK_DIVIDE * CLKOUT_DIVIDE[k] * 1.0) / CLKFBOUT_MULT))) begin 496 | $display("PASSED: CLKOUT%0d frequency", k); 497 | pass_count = pass_count + 1; 498 | end else begin 499 | $display("FAILED: CLKOUT%0d frequency", k); 500 | fail_count = fail_count + 1; 501 | end 502 | end 503 | 504 | if ((period_1000_fb) == (1000 * `CLKIN1_PERIOD * ((DIVCLK_DIVIDE * 1.0) / CLKFBOUT_MULT))) begin 505 | $display("PASSED: CLKFBOUT frequency"); 506 | pass_count = pass_count + 1; 507 | end else begin 508 | $display("FAILED: CLKFBOUT frequency"); 509 | fail_count = fail_count + 1; 510 | end 511 | 512 | 513 | /*------- DUTY CYCLE ---------*/ 514 | for (k = 0; k <= 5; k = k + 1) begin 515 | if (dcc_fail[k] !== 1'b1) begin 516 | $display("PASSED: CLKOUT%0d duty cycle", k); 517 | pass_count = pass_count + 1; 518 | end else begin 519 | $display("FAILED: CLKOUT%0d duty cycle", k); 520 | fail_count = fail_count + 1; 521 | end 522 | end 523 | 524 | /*------- PHASE SHIFT ---------*/ 525 | for (k = 0; k <= 5; k = k + 1) begin 526 | if (psc_fail[k] !== 1'b1) begin 527 | $display("PASSED: CLKOUT%0d phase shift", k); 528 | pass_count = pass_count + 1; 529 | end else begin 530 | $display("FAILED: CLKOUT%0d phase shift", k); 531 | fail_count = fail_count + 1; 532 | end 533 | end 534 | 535 | if (psc_fail_fb !== 1'b1) begin 536 | $display("PASSED: CLKFBOUT phase shift"); 537 | pass_count = pass_count + 1; 538 | end else begin 539 | $display("FAILED: CLKFBOUT phase shift"); 540 | fail_count = fail_count + 1; 541 | end 542 | 543 | 544 | PWRDWN = 1; 545 | #100; 546 | if ((CLKOUT[0] & CLKOUT[1] & CLKOUT[2] & CLKOUT[3] & CLKOUT[4] & CLKOUT[5] & CLKFBOUT) === 1'bx) begin 547 | $display("PASSED: PWRDWN"); 548 | pass_count = pass_count + 1; 549 | end else begin 550 | $display("FAILED: PWRDWN"); 551 | fail_count = fail_count + 1; 552 | end 553 | 554 | if ((pass_count + fail_count) == total) begin 555 | $display("PASSED: number of test cases"); 556 | pass_count = pass_count + 1; 557 | end else begin 558 | $display("FAILED: number of test cases"); 559 | fail_count = fail_count + 1; 560 | end 561 | 562 | $display("%0d/%0d PASSED", pass_count, (total + 1)); 563 | 564 | $finish; 565 | end 566 | 567 | /* connect CLKFBIN with CLKFBOUT to use internal feedback */ 568 | assign CLKFBIN = CLKFBOUT; 569 | 570 | always #(`CLKIN1_PERIOD / 2.0) CLKIN1 = ~CLKIN1; 571 | always #(`CLKIN2_PERIOD / 2.0) CLKIN2 = ~CLKIN2; 572 | always #(`DCLK_PERIOD / 2.0) DCLK = ~DCLK; 573 | 574 | 575 | /* calculate the dynamic values */ 576 | wire [31:0] CLKOUT_DIVIDE_DYN[0:5]; 577 | wire [31:0] CLKOUT_DUTY_CYCLE_DYN_1000[0:5]; 578 | wire [31:0] CLKOUT_PHASE_DYN[0:5]; 579 | wire [31:0] CLKFBOUT_MULT_DYN_1000; 580 | wire [31:0] CLKFBOUT_PHASE_DYN; 581 | wire [31:0] DIVCLK_DIVIDE_DYN; 582 | 583 | dyn_reconf dyn_reconf ( 584 | .RST(RST), 585 | .PWRDWN(PWRDWN), 586 | 587 | .vco_period_1000(period_1000_fb), 588 | 589 | .DADDR(DADDR), 590 | .DCLK(DCLK), 591 | .DEN(DEN), 592 | .DWE(DWE), 593 | .DI(DI), 594 | .DO(DO), 595 | .DRDY(DRDY), 596 | 597 | .CLKOUT0_DIVIDE(CLKOUT_DIVIDE_DYN[0]), 598 | .CLKOUT0_DUTY_CYCLE_1000(CLKOUT_DUTY_CYCLE_DYN_1000[0]), 599 | .CLKOUT0_PHASE(CLKOUT_PHASE_DYN[0]), 600 | 601 | .CLKOUT1_DIVIDE(CLKOUT_DIVIDE_DYN[1]), 602 | .CLKOUT1_DUTY_CYCLE_1000(CLKOUT_DUTY_CYCLE_DYN_1000[1]), 603 | .CLKOUT1_PHASE(CLKOUT_PHASE_DYN[1]), 604 | 605 | .CLKOUT2_DIVIDE(CLKOUT_DIVIDE_DYN[2]), 606 | .CLKOUT2_DUTY_CYCLE_1000(CLKOUT_DUTY_CYCLE_DYN_1000[2]), 607 | .CLKOUT2_PHASE(CLKOUT_PHASE_DYN[2]), 608 | 609 | .CLKOUT3_DIVIDE(CLKOUT_DIVIDE_DYN[3]), 610 | .CLKOUT3_DUTY_CYCLE_1000(CLKOUT_DUTY_CYCLE_DYN_1000[3]), 611 | .CLKOUT3_PHASE(CLKOUT_PHASE_DYN[3]), 612 | 613 | .CLKOUT4_DIVIDE(CLKOUT_DIVIDE_DYN[4]), 614 | .CLKOUT4_DUTY_CYCLE_1000(CLKOUT_DUTY_CYCLE_DYN_1000[4]), 615 | .CLKOUT4_PHASE(CLKOUT_PHASE_DYN[4]), 616 | 617 | .CLKOUT5_DIVIDE(CLKOUT_DIVIDE_DYN[5]), 618 | .CLKOUT5_DUTY_CYCLE_1000(CLKOUT_DUTY_CYCLE_DYN_1000[5]), 619 | .CLKOUT5_PHASE(CLKOUT_PHASE_DYN[5]), 620 | 621 | .CLKFBOUT_MULT_F_1000(CLKFBOUT_MULT_DYN_1000), 622 | .CLKFBOUT_PHASE(CLKFBOUT_PHASE_DYN), 623 | 624 | .DIVCLK_DIVIDE(DIVCLK_DIVIDE_DYN)); 625 | 626 | 627 | integer l; 628 | /* set the internal values to the dynamically set */ 629 | always @* begin 630 | for (l = 0; l <= 5; l = l + 1) begin 631 | if (CLKOUT_DIVIDE_DYN[l] != 0) 632 | CLKOUT_DIVIDE[l] = CLKOUT_DIVIDE_DYN[l]; 633 | if (CLKOUT_DUTY_CYCLE_DYN_1000[l] != 0) 634 | CLKOUT_DUTY_CYCLE_1000[l] = CLKOUT_DUTY_CYCLE_DYN_1000[l]; 635 | if (CLKOUT_PHASE_DYN[l] != 0) 636 | CLKOUT_PHASE_1000[l] = CLKOUT_PHASE_DYN[l]; 637 | end 638 | if (CLKFBOUT_MULT_DYN_1000 != 0) 639 | CLKFBOUT_MULT = CLKFBOUT_MULT_DYN_1000 / 1000; 640 | if (CLKFBOUT_PHASE_DYN != 0) 641 | CLKFBOUT_PHASE = CLKFBOUT_PHASE_DYN; 642 | if (DIVCLK_DIVIDE_DYN != 0) 643 | DIVCLK_DIVIDE = DIVCLK_DIVIDE_DYN; 644 | end 645 | endmodule 646 | --------------------------------------------------------------------------------