diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aab8220f..cfe15866 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -98,7 +98,6 @@ jobs: ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/i2c_slave/v1_0/i2c_slave_gen.py --data_width=32 --build-name=wrapper --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/priority_encoder/v1_0/priority_encoder_gen.py --width=7 --build-name=encoder --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/vexriscv_cpu/v1_0/vexriscv_cpu_gen.py --build-name=vexriscv_wrap --build-dir=./ --build - ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axis_width_converter/v1_0/axis_width_converter_gen.py --core_in_width=1024 --core_out_width=32 --build-name=width_converter --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axi_interconnect/v1_0/axi_interconnect_gen.py --data_width=32 --addr_width=64 --s_count=7 --m_count=4 --build-name=interconnect_wrapper --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axil_interconnect/v1_0/axil_interconnect_gen.py --data_width=32 --addr_width=64 --s_count=5 --m_count=2 --build-name=interconnect_wrapper --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axil_crossbar/v1_0/axil_crossbar_gen.py --data_width=32 --addr_width=64 --s_count=5 --m_count=6 --build-name=crossbar_wrapper --build @@ -206,7 +205,6 @@ jobs: ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/i2c_slave/v1_0/i2c_slave_gen.py --data_width=32 --json-template --build-name=wrapper --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/priority_encoder/v1_0/priority_encoder_gen.py --width=7 --json-template --build-name=encoder --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/vexriscv_cpu/v1_0/vexriscv_cpu_gen.py --json-template --build-name=vexriscv_wrap --build-dir=./ --build - ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axis_width_converter/v1_0/axis_width_converter_gen.py --core_in_width=1024 --core_out_width=32 --json-template --build-name=width_converter --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axi_interconnect/v1_0/axi_interconnect_gen.py --data_width=32 --addr_width=64 --s_count=7 --m_count=4 --json-template --build-name=interconnect_wrapper --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axil_interconnect/v1_0/axil_interconnect_gen.py --data_width=32 --addr_width=64 --s_count=5 --m_count=2 --json-template --build-name=interconnect_wrapper --build ./python3 $GITHUB_WORKSPACE/rapidsilicon/ip/axil_crossbar/v1_0/axil_crossbar_gen.py --data_width=32 --addr_width=64 --s_count=5 --m_count=6 --json-template --build-name=crossbar_wrapper --build diff --git a/rapidsilicon/ip/axis_width_converter/v1_0/README.md b/rapidsilicon/ip/axis_width_converter/v1_0/README.md deleted file mode 100644 index 62793074..00000000 --- a/rapidsilicon/ip/axis_width_converter/v1_0/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# AXI STREAM WIDTH CONVERTER Core Generation - -## Introduction -AXIS-WIDTH-CONVERTER is AXI4-Stream based IP core. - -## Generator Script -This directory contains the generator script which generates the RTL to `rapidsilicon/ip/axis_width_converter/v1_0//src/` directory. - -## Parameters -User can configure AXIS_WIDTH_CONVERTER CORE using following parameters: - -| Sr.No.| Parameter | Keyword | Value | -|-------|--------------------|--------------------|---------------------------| -| 1. | CORE_IN_WIDTH | core_in_width | 8,16,32,64,128,512,1024 | -| 2. | CORE_OUT_WIDTH | core_out_width | 8,16,32,64,128,512,1024 | -| 3. | CORE_USER_WIDTH | core_user_width | 1-4096 | -| 4. | CORE_REVERSE | core_reverse | 0/1 | - - -To generate RTL with above parameters, run the following command: -``` -python3 axis_width_converter_gen.py --core_in_width=1024 --core_out_width=32 --build-name=width_converter --build -``` - -## TCL File -This python script also generates a raptor.tcl file which will be placed in `rapidsilicon/ip/axis_width_converter/v1_0//synth/` directory. - - diff --git a/rapidsilicon/ip/axis_width_converter/v1_0/axis_width_converter_gen.py b/rapidsilicon/ip/axis_width_converter/v1_0/axis_width_converter_gen.py deleted file mode 100755 index 4a40d8aa..00000000 --- a/rapidsilicon/ip/axis_width_converter/v1_0/axis_width_converter_gen.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python3 -# -# This file is Copyright (c) 2022 RapidSilicon. -# -# SPDX-License-Identifier: MIT - -import os -import sys -import argparse - -from migen import * - -from litex.build.generic_platform import * - -from litex.build.osfpga import OSFPGAPlatform - -from litex.soc.interconnect import stream - -from litex.soc.interconnect.axi import * - -# IOs/Interfaces ----------------------------------------------------------------------------------- -def get_clkin_ios(): - return [ - ("axis_clk", 0, Pins(1)), - ("axis_rst", 0, Pins(1)), - ] - -# AXI Stream Converter ----------------------------------------------------------------------------- -class AXISConverter(Module): - def __init__(self, platform, in_width=64, out_width=64, user_width=0, reverse=False): - # Clocking --------------------------------------------------------------------------------- - platform.add_extension(get_clkin_ios()) - self.clock_domains.cd_sys = ClockDomain() - self.comb += self.cd_sys.clk.eq(platform.request("axis_clk")) - self.comb += self.cd_sys.rst.eq(platform.request("axis_rst")) - - # Input AXI -------------------------------------------------------------------------------- - axis_in = AXIStreamInterface(data_width=in_width, user_width=user_width) - platform.add_extension(axis_in.get_ios("s_axis")) - self.comb += axis_in.connect_to_pads(platform.request("s_axis"), mode="slave") - - # Output AXI ------------------------------------------------------------------------------- - axis_out = AXIStreamInterface(data_width=out_width, user_width=user_width) - platform.add_extension(axis_out.get_ios("m_axis")) - self.comb += axis_out.connect_to_pads(platform.request("m_axis"), mode="master") - - # Converter -------------------------------------------------------------------------------- - converter = stream.StrideConverter(axis_in.description, axis_out.description, reverse=reverse) - self.submodules += converter - self.comb += axis_in.connect(converter.sink) - self.comb += converter.source.connect(axis_out) - -# Build -------------------------------------------------------------------------------------------- -def main(): - parser = argparse.ArgumentParser(description="AXI STREAM CONVERTER CORE") - - # Import Common Modules. - common_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "lib") - sys.path.append(common_path) - - from common import IP_Builder - - # Parameter Dependency dictionary - # Ports : Dependency - dep_dict = {} - - # IP Builder. - rs_builder = IP_Builder(device="gemini", ip_name="axis_width_converter", language="verilog") - - # Core fix value parameters. - core_fix_param_group = parser.add_argument_group(title="Core fix parameters") - core_fix_param_group.add_argument("--core_in_width", type=int, default=128, choices=[8, 16, 32, 64, 128, 256, 512, 1024], help="AXI-ST Input Data-width.") - core_fix_param_group.add_argument("--core_out_width", type=int, default=64, choices=[8, 16, 32, 64, 128, 256, 512, 1024], help="AXI-ST Output Data-width.") - - # Core bool value parameters. - core_bool_param_group = parser.add_argument_group(title="Core bool parameters") - core_bool_param_group.add_argument("--core_reverse", type=bool, default=False, help="Reverse Converter Ordering.") - - # Core range value parameters. - core_range_param_group = parser.add_argument_group(title="Core range parameters") - core_range_param_group.add_argument("--core_user_width", type=int, default=1, choices=range(1,4097), help="AXI-ST User width.") - - - # Build Parameters. - build_group = parser.add_argument_group(title="Build parameters") - build_group.add_argument("--build", action="store_true", help="Build core.") - build_group.add_argument("--build-dir", default="./", help="Build directory.") - build_group.add_argument("--build-name", default="axis_width_converter", help="Build Folder Name, Build RTL File Name and Module Name") - - # JSON Import/Template - json_group = parser.add_argument_group(title="JSON parameters") - json_group.add_argument("--json", help="Generate core from JSON file.") - json_group.add_argument("--json-template", action="store_true", help="Generate JSON template.") - - args = parser.parse_args() - - # Import JSON (Optional) ----------------------------------------------------------------------- - if args.json: - args = rs_builder.import_args_from_json(parser=parser, json_filename=args.json) - - # Export JSON Template (Optional) -------------------------------------------------------------- - if args.json_template: - rs_builder.export_json_template(parser=parser, dep_dict=dep_dict) - - # Create LiteX Core ---------------------------------------------------------------------------- - core_in_width = int(args.core_in_width) - core_out_width = int(args.core_out_width) - core_user_width = int(args.core_user_width) - platform = OSFPGAPlatform(io=[], toolchain="raptor", device="gemini") - module = AXISConverter(platform, - in_width = core_in_width, - out_width = core_out_width, - user_width = core_user_width, - ) - - # Build Project -------------------------------------------------------------------------------- - if args.build: - rs_builder.prepare( - build_dir = args.build_dir, - build_name = args.build_name, - version = "v1_0" - ) - rs_builder.copy_files(gen_path=os.path.dirname(__file__)) - rs_builder.generate_tcl() - rs_builder.generate_wrapper( - platform = platform, - module = module, - ) - -if __name__ == "__main__": - main() diff --git a/rapidsilicon/ip/axis_width_converter/v1_0/docs/axis_width_converter.pdf b/rapidsilicon/ip/axis_width_converter/v1_0/docs/axis_width_converter.pdf deleted file mode 100644 index 4bee90b2..00000000 Binary files a/rapidsilicon/ip/axis_width_converter/v1_0/docs/axis_width_converter.pdf and /dev/null differ diff --git a/rapidsilicon/ip/axis_width_converter/v1_0/sim/README.md b/rapidsilicon/ip/axis_width_converter/v1_0/sim/README.md deleted file mode 100644 index 5c33740b..00000000 --- a/rapidsilicon/ip/axis_width_converter/v1_0/sim/README.md +++ /dev/null @@ -1 +0,0 @@ -Directory for Simulation Files \ No newline at end of file diff --git a/rapidsilicon/ip/boot_clock/v1_0/boot_clock_gen.py b/rapidsilicon/ip/boot_clock/v1_0/boot_clock_gen.py new file mode 100755 index 00000000..438a6c73 --- /dev/null +++ b/rapidsilicon/ip/boot_clock/v1_0/boot_clock_gen.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# +# This file is Copyright (c) 2022 RapidSilicon. +# +# SPDX-License-Identifier: MIT + +import os +import sys +import logging +import argparse + +from litex_wrapper.boot_clock_litex_wrapper import BOOT_CLOCK + +from migen import * + +from litex.build.generic_platform import * + +from litex.build.osfpga import OSFPGAPlatform + +def get_clkin_ios(): + return [ + ("clk", 0, Pins(1)), + ("rst", 0, Pins(1)) + ] + +# IOs/Interfaces ----------------------------------------------------------------------------------- + +def get_other_ios(): + return [ + ("O", 0, Pins(1)), + ] + +# AXI RAM Wrapper ---------------------------------------------------------------------------------- +class BOOTCLOCKWrapper(Module): + def __init__(self, platform, period): + self.clock_domains.cd_sys = ClockDomain() + + + # Boot Clock ---------------------------------------------------------------------------------- + self.submodules.boot_clock = boot_clock = BOOT_CLOCK(platform, period = period + ) + + # Ports --------------------------------------------------------------------------------- + platform.add_extension(get_other_ios()) + self.comb += platform.request("O").eq(boot_clock.O) + +# Build -------------------------------------------------------------------------------------------- +def main(): + parser = argparse.ArgumentParser(description="Boot CLock") + + # Import Common Modules. + common_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "lib") + sys.path.append(common_path) + + from common import IP_Builder + + # Parameter Dependency dictionary + # Ports : Dependency + dep_dict = {} + + # IP Builder. + rs_builder = IP_Builder(device="gemini", ip_name="boot_clock", language="verilog") + + logging.info("===================================================") + logging.info("IP : %s", rs_builder.ip_name.upper()) + logging.info(("===================================================")) + + + # Core range value parameters. + core_range_param_group = parser.add_argument_group(title="Core range parameters") + core_range_param_group.add_argument("--period", type=int, default=25, choices=range(1,1000), help="Clock period in ns") + + + # Build Parameters. + build_group = parser.add_argument_group(title="Build parameters") + build_group.add_argument("--build", action="store_true", help="Build Core") + build_group.add_argument("--build-dir", default="./", help="Build Directory") + build_group.add_argument("--build-name", default="boot_clock_wrapper", help="Build Folder Name, Build RTL File Name and Module Name") + + # JSON Import/Template + json_group = parser.add_argument_group(title="JSON Parameters") + json_group.add_argument("--json", help="Generate Core from JSON File") + json_group.add_argument("--json-template", action="store_true", help="Generate JSON Template") + + args = parser.parse_args() + + # Import JSON (Optional) ----------------------------------------------------------------------- + if args.json: + args = rs_builder.import_args_from_json(parser=parser, json_filename=args.json) + + # Export JSON Template (Optional) -------------------------------------------------------------- + if args.json_template: + rs_builder.export_json_template(parser=parser, dep_dict=dep_dict) + + # Create Wrapper ------------------------------------------------------------------------------- + platform = OSFPGAPlatform(io=[], toolchain="raptor", device="gemini") + module = BOOTCLOCKWrapper(platform, + period = args.period, + ) + + # Build Project -------------------------------------------------------------------------------- + if args.build: + rs_builder.prepare( + build_dir = args.build_dir, + build_name = args.build_name, + version = "v1_0" + ) + rs_builder.copy_files(gen_path=os.path.dirname(__file__)) + rs_builder.generate_tcl() + rs_builder.generate_wrapper( + platform = platform, + module = module, + ) + +if __name__ == "__main__": + main() diff --git a/rapidsilicon/ip/boot_clock/v1_0/litex_wrapper/README.md b/rapidsilicon/ip/boot_clock/v1_0/litex_wrapper/README.md new file mode 100644 index 00000000..a401ce46 --- /dev/null +++ b/rapidsilicon/ip/boot_clock/v1_0/litex_wrapper/README.md @@ -0,0 +1 @@ +Directory for Python Wrapper diff --git a/rapidsilicon/ip/boot_clock/v1_0/litex_wrapper/boot_clock_litex_wrapper.py b/rapidsilicon/ip/boot_clock/v1_0/litex_wrapper/boot_clock_litex_wrapper.py new file mode 100644 index 00000000..858151b1 --- /dev/null +++ b/rapidsilicon/ip/boot_clock/v1_0/litex_wrapper/boot_clock_litex_wrapper.py @@ -0,0 +1,56 @@ +# +# This file is part of RapidSilicon's IP_Catalog. +# +# This file is Copyright (c) 2022 RapidSilicon. +# +# SPDX-License-Identifier: MIT +# + +import os +import datetime +import logging + +from migen import * + +# logging.basicConfig(level=logging.INFO) +logging.basicConfig(filename="IP.log",filemode="w", level=logging.INFO, format='%(levelname)s: %(message)s\n') + +timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +logging.info(f'Log started at {timestamp}') + + +def colorer(s, color="bright"): + header = { + "bright": "\x1b[1m", + "green": "\x1b[32m", + "cyan": "\x1b[36m", + "red": "\x1b[31m", + "yellow": "\x1b[33m", + "underline": "\x1b[4m"}[color] + trailer = "\x1b[0m" + return header + str(s) + trailer + + +class BOOT_CLOCK(Module): + def __init__(self, platform, period): + self.logger = logging.getLogger("BOOT_CLOCK") + self.logger.propagate = True + + self.logger.info("Creating BOOT_CLOCK module.") + self.logger.info(f"=================== PARAMETERS ====================") + self.logger.info(f"PERIOD : {colorer(period)}") + + # Clock output + self.O = Signal() + + self.specials += Instance("BOOT_CLOCK", + p_PERIOD=Instance.PreformattedParam(period), + o_O=self.O + ) + + self.add_sources(platform) + + @staticmethod + def add_sources(platform): + rtl_dir = os.path.join(os.path.dirname(__file__), "../src") + platform.add_source(os.path.join(rtl_dir, "boot_clock.v")) \ No newline at end of file diff --git a/rapidsilicon/ip/axis_width_converter/v1_0/sdc/README.md b/rapidsilicon/ip/boot_clock/v1_0/sdc/README.md similarity index 100% rename from rapidsilicon/ip/axis_width_converter/v1_0/sdc/README.md rename to rapidsilicon/ip/boot_clock/v1_0/sdc/README.md diff --git a/rapidsilicon/ip/boot_clock/v1_0/src/BOOT_CLOCK.v b/rapidsilicon/ip/boot_clock/v1_0/src/BOOT_CLOCK.v new file mode 100644 index 00000000..de276cbe --- /dev/null +++ b/rapidsilicon/ip/boot_clock/v1_0/src/BOOT_CLOCK.v @@ -0,0 +1,30 @@ +`timescale 1ns/1ps +`celldefine +// +// BOOT_CLOCK simulation model +// Internal BOOT_CLK connection +// +// Copyright (c) 2023 Rapid Silicon, Inc. All rights reserved. +// + +module BOOT_CLOCK #( + parameter PERIOD = 25.0 // Clock period for simulation purposes (nS) +) ( + output reg O = 1'b0 // Clock output +); +localparam HALF_PERIOD = PERIOD/2.0; + + + always + #HALF_PERIOD O <= ~O; + initial begin + + if ((PERIOD < 16.0) || (PERIOD > 30.0)) begin + $display("BOOT_CLOCK instance %m PERIOD set to incorrect value, %f. Values must be between 16.0 and 30.0.", PERIOD); + #1 $stop; + end + + end + +endmodule +`endcelldefine diff --git a/rapidsilicon/ip/axis_width_converter/v1_0/LICENSE b/rapidsilicon/ip/pll/v1_0/LICENSE similarity index 90% rename from rapidsilicon/ip/axis_width_converter/v1_0/LICENSE rename to rapidsilicon/ip/pll/v1_0/LICENSE index 00b3450c..3bfbbc26 100644 --- a/rapidsilicon/ip/axis_width_converter/v1_0/LICENSE +++ b/rapidsilicon/ip/pll/v1_0/LICENSE @@ -21,6 +21,8 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. FILES: -rapidsilicon/ip/axis_width_converter/v1_0/axis_width_converter_gen.py - +rapidsilicon/ip/pll/v1_0/pll_gen.py +rapidsilicon/ip/pll/v1_0/litex_wrapper/pll_litex_wrapper.py +rapidsilicon/ip/pll/v1_0/src/pll.v --------------------------------------------------------------------------------------- + diff --git a/rapidsilicon/ip/pll/v1_0/docs/axi_ram.pdf b/rapidsilicon/ip/pll/v1_0/docs/axi_ram.pdf new file mode 100644 index 00000000..e94264e7 Binary files /dev/null and b/rapidsilicon/ip/pll/v1_0/docs/axi_ram.pdf differ diff --git a/rapidsilicon/ip/pll/v1_0/litex_wrapper/README.md b/rapidsilicon/ip/pll/v1_0/litex_wrapper/README.md new file mode 100644 index 00000000..a401ce46 --- /dev/null +++ b/rapidsilicon/ip/pll/v1_0/litex_wrapper/README.md @@ -0,0 +1 @@ +Directory for Python Wrapper diff --git a/rapidsilicon/ip/pll/v1_0/litex_wrapper/pll_litex_wrapper.py b/rapidsilicon/ip/pll/v1_0/litex_wrapper/pll_litex_wrapper.py new file mode 100644 index 00000000..0b6a79b8 --- /dev/null +++ b/rapidsilicon/ip/pll/v1_0/litex_wrapper/pll_litex_wrapper.py @@ -0,0 +1,106 @@ +# +# This file is part of RapidSilicon's IP_Catalog. +# +# This file is Copyright (c) 2022 RapidSilicon. +# +# SPDX-License-Identifier: MIT +# + + +import os +import datetime +import logging + +from migen import * + +from litex.soc.interconnect.axi import * + +# logging.basicConfig(level=logging.INFO) +logging.basicConfig(filename="IP.log",filemode="w", level=logging.INFO, format='%(levelname)s: %(message)s\n') + +timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +logging.info(f'Log started at {timestamp}') + +# Helpers ------------------------------------------------------------------------------------------ +class Open(Signal): pass + +def colorer(s, color="bright"): + header = { + "bright": "\x1b[1m", + "green": "\x1b[32m", + "cyan": "\x1b[36m", + "red": "\x1b[31m", + "yellow": "\x1b[33m", + "underline": "\x1b[4m"}[color] + trailer = "\x1b[0m" + return header + str(s) + trailer + +# PLL Wrapper ------------------------------------------------------------------------------------ + +class PLL(Module): + def __init__(self, platform, divide_clk_in_by_2, pll_mult, pll_div, clk_out0_div, clk_out1_div, clk_out2_div, clk_out3_div, **kwargs): + self.logger = logging.getLogger("PLL") + self.logger.propagate = True + + + # Logger + self.logger = logging.getLogger("PLL") + + self.logger.propagate = True + + self.logger.info(f"=================== PARAMETERS ====================") + + self.logger.info(f"DIVIDE_CLK_IN_BY_2 : {divide_clk_in_by_2}") + self.logger.info(f"PLL_MULT : {pll_mult}") + self.logger.info(f"PLL_DIV : {pll_div}") + self.logger.info(f"CLK_OUT0_DIV : {clk_out0_div}") + self.logger.info(f"CLK_OUT1_DIV : {clk_out1_div}") + self.logger.info(f"CLK_OUT2_DIV : {clk_out2_div}") + self.logger.info(f"CLK_OUT3_DIV : {clk_out3_div}") + self.logger.info(f"===================================================") + + # Create input/output signals + self.PLL_EN = Signal() + self.CLK_IN = Signal() + self.CLK_OUT0_EN = Signal() + self.CLK_OUT1_EN = Signal() + self.CLK_OUT2_EN = Signal() + self.CLK_OUT3_EN = Signal() + self.CLK_OUT0 = Signal() + self.CLK_OUT1 = Signal() + self.CLK_OUT2 = Signal() + self.CLK_OUT3 = Signal() + self.GEARBOX_FAST_CLK = Signal() + self.LOCK = Signal() + + self.specials += Instance("PLL", + **kwargs, + + p_DIVIDE_CLK_IN_BY_2 = Instance.PreformattedParam(divide_clk_in_by_2), + p_PLL_MULT = Instance.PreformattedParam(pll_mult), + p_PLL_DIV = Instance.PreformattedParam(pll_div), + p_CLK_OUT0_DIV = Instance.PreformattedParam(clk_out0_div), + p_CLK_OUT1_DIV = Instance.PreformattedParam(clk_out1_div), + p_CLK_OUT2_DIV = Instance.PreformattedParam(clk_out2_div), + p_CLK_OUT3_DIV = Instance.PreformattedParam(clk_out3_div), + + i_PLL_EN = self.PLL_EN, + i_CLK_IN = self.CLK_IN, + i_CLK_OUT0_EN = self.CLK_OUT0_EN, + i_CLK_OUT1_EN = self.CLK_OUT1_EN, + i_CLK_OUT2_EN = self.CLK_OUT2_EN, + i_CLK_OUT3_EN = self.CLK_OUT3_EN, + o_CLK_OUT0 = self.CLK_OUT0, + o_CLK_OUT1 = self.CLK_OUT1, + o_CLK_OUT2 = self.CLK_OUT2, + o_CLK_OUT3 = self.CLK_OUT3, + o_GEARBOX_FAST_CLK = self.GEARBOX_FAST_CLK, + o_LOCK = self.LOCK + ) + + self.add_sources(platform) + + @staticmethod + def add_sources(platform): + rtl_dir = os.path.join(os.path.dirname(__file__), "../src") + platform.add_source(os.path.join(rtl_dir, "PLL.v")) diff --git a/rapidsilicon/ip/pll/v1_0/pll_gen.py b/rapidsilicon/ip/pll/v1_0/pll_gen.py new file mode 100755 index 00000000..aa1b99b3 --- /dev/null +++ b/rapidsilicon/ip/pll/v1_0/pll_gen.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# +# This file is Copyright (c) 2022 RapidSilicon. +# +# SPDX-License-Identifier: MIT + +import os +import sys +import logging +import argparse + +from litex_wrapper.pll_litex_wrapper import PLL + +from migen import * + +from litex.build.generic_platform import * + +from litex.build.osfpga import OSFPGAPlatform + + + +# IOs/Interfaces ----------------------------------------------------------------------------------- + +def get_clkin_ios(): + return [ + ("PLL_EN", 0, Pins(1)), + ("CLK_IN", 0, Pins(1)), + ("CLK_OUT0_EN", 0, Pins(1)), + ("CLK_OUT1_EN", 0, Pins(1)), + ("CLK_OUT2_EN", 0, Pins(1)), + ("CLK_OUT3_EN", 0, Pins(1)), + ("CLK_OUT0", 0, Pins(1)), + ("CLK_OUT1", 0, Pins(1)), + ("CLK_OUT2", 0, Pins(1)), + ("CLK_OUT3", 0, Pins(1)), + ("GEARBOX_FAST_CLK",0, Pins(1)), + ("LOCK", 0, Pins(1)), + ] + +# AXI RAM Wrapper ---------------------------------------------------------------------------------- +class PLLWrapper(Module): + def __init__(self, platform, divide_clk_in_by_2, pll_mult, pll_div, clk_out0_div, clk_out1_div, clk_out2_div, clk_out3_div): + + self.clock_domains.cd_sys = ClockDomain() + + # AXI-RAM ---------------------------------------------------------------------------------- + self.submodules.pll = pll = PLL(platform, + divide_clk_in_by_2 = divide_clk_in_by_2, + pll_mult = pll_mult, + pll_div = pll_div, + clk_out0_div = clk_out0_div, + clk_out1_div = clk_out1_div, + clk_out2_div = clk_out2_div, + clk_out3_div = clk_out3_div, + ) + + platform.add_extension(get_clkin_ios()) + self.comb += pll.PLL_EN.eq(platform.request("PLL_EN")) + self.comb += pll.CLK_IN.eq(platform.request("CLK_IN")) + self.comb += pll.CLK_OUT0_EN.eq(platform.request("CLK_OUT0_EN")) + self.comb += pll.CLK_OUT1_EN.eq(platform.request("CLK_OUT1_EN")) + self.comb += pll.CLK_OUT2_EN.eq(platform.request("CLK_OUT2_EN")) + self.comb += pll.CLK_OUT3_EN.eq(platform.request("CLK_OUT3_EN")) + + self.comb += platform.request("CLK_OUT0").eq(pll.CLK_OUT0) + self.comb += platform.request("CLK_OUT1").eq(pll.CLK_OUT1) + self.comb += platform.request("CLK_OUT2").eq(pll.CLK_OUT2) + self.comb += platform.request("CLK_OUT3").eq(pll.CLK_OUT3) + self.comb += platform.request("GEARBOX_FAST_CLK").eq(pll.GEARBOX_FAST_CLK) + self.comb += platform.request("LOCK").eq(pll.LOCK) + +# Build -------------------------------------------------------------------------------------------- +def main(): + parser = argparse.ArgumentParser(description="PLL CORE") + + # Import Common Modules. + common_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "lib") + sys.path.append(common_path) + + from common import IP_Builder + + # Parameter Dependency dictionary + # Ports : Dependency + dep_dict = {} + + # IP Builder. + rs_builder = IP_Builder(device="gemini", ip_name="pll", language="verilog") + + logging.info("===================================================") + logging.info("IP : %s", rs_builder.ip_name.upper()) + logging.info(("===================================================")) + + + + # Core fix value parameters. + core_fix_param_group = parser.add_argument_group(title="Core fix parameters") + core_fix_param_group.add_argument("--clk_out0_div", type=int, default=2, choices=[2,3,4,5,6,7,8,10,12,16,20.24,32,40,48,64], help="CLK_OUT0 divider value") + core_fix_param_group.add_argument("--clk_out1_div", type=int, default=2, choices=[2,3,4,5,6,7,8,10,12,16,20.24,32,40,48,64], help="CLK_OUT1 divider value") + core_fix_param_group.add_argument("--clk_out2_div", type=int, default=2, choices=[2,3,4,5,6,7,8,10,12,16,20.24,32,40,48,64], help="CLK_OUT2 divider value") + core_fix_param_group.add_argument("--clk_out3_div", type=int, default=2, choices=[2,3,4,5,6,7,8,10,12,16,20.24,32,40,48,64], help="CLK_OUT3 divider value") + + # Core range value parameters. + core_range_param_group = parser.add_argument_group(title="Core range parameters") + core_range_param_group.add_argument("--pll_mult", type=int, default=16, choices=range(16,1000), help="RAM Address Width") + core_range_param_group.add_argument("--pll_div", type=int, default=1, choices=range(1, 63), help="RAM ID Width") + + # Core bool value parameters. + core_bool_param_group = parser.add_argument_group(title="Core bool parameters") + core_bool_param_group.add_argument("--divide_clk_in_by_2", type=bool, default=False, help="RAM Pipelined Output") + + # Core file path parameters. + # core_file_path_group = parser.add_argument_group(title="Core file path parameters") + # core_file_path_group.add_argument("--file_path", type=argparse.FileType('r'), help="File Path for memory initialization file") + + # Build Parameters. + build_group = parser.add_argument_group(title="Build parameters") + build_group.add_argument("--build", action="store_true", help="Build Core") + build_group.add_argument("--build-dir", default="./", help="Build Directory") + build_group.add_argument("--build-name", default="pll_wrapper", help="Build Folder Name, Build RTL File Name and Module Name") + + # JSON Import/Template + json_group = parser.add_argument_group(title="JSON Parameters") + json_group.add_argument("--json", help="Generate Core from JSON File") + json_group.add_argument("--json-template", action="store_true", help="Generate JSON Template") + + args = parser.parse_args() + + # Import JSON (Optional) ----------------------------------------------------------------------- + if args.json: + args = rs_builder.import_args_from_json(parser=parser, json_filename=args.json) + + # Export JSON Template (Optional) -------------------------------------------------------------- + if args.json_template: + rs_builder.export_json_template(parser=parser, dep_dict=dep_dict) + + # Create Wrapper ------------------------------------------------------------------------------- + platform = OSFPGAPlatform(io=[], toolchain="raptor", device="gemini") + module = PLLWrapper(platform, + divide_clk_in_by_2=args.divide_clk_in_by_2, + pll_mult=args.pll_mult, + pll_div=args.pll_div, + clk_out0_div=args.clk_out0_div, + clk_out1_div=args.clk_out1_div, + clk_out2_div=args.clk_out2_div, + clk_out3_div=args.clk_out3_div) + + # Build Project -------------------------------------------------------------------------------- + if args.build: + rs_builder.prepare( + build_dir = args.build_dir, + build_name = args.build_name, + version = "v1_0" + ) + rs_builder.copy_files(gen_path=os.path.dirname(__file__)) + rs_builder.generate_tcl() + rs_builder.generate_wrapper( + platform = platform, + module = module, + ) + +if __name__ == "__main__": + main() diff --git a/rapidsilicon/ip/pll/v1_0/sdc/README.md b/rapidsilicon/ip/pll/v1_0/sdc/README.md new file mode 100644 index 00000000..19d0e63e --- /dev/null +++ b/rapidsilicon/ip/pll/v1_0/sdc/README.md @@ -0,0 +1 @@ +Directory for Constraints \ No newline at end of file diff --git a/rapidsilicon/ip/pll/v1_0/src/PLL.v b/rapidsilicon/ip/pll/v1_0/src/PLL.v new file mode 100644 index 00000000..27ec23ed --- /dev/null +++ b/rapidsilicon/ip/pll/v1_0/src/PLL.v @@ -0,0 +1,313 @@ +`timescale 1ns/1ps +`celldefine +// +// PLL simulation model +// Phase locked loop +// +// Copyright (c) 2023 Rapid Silicon, Inc. All rights reserved. +// + +module post_div #( + parameter div = 2 +)( + input fref, + input mes_done, + input rst_n, + input realtime vco_time, + output logic fout +); + logic fc; + clk_gen post_div_clk_gen + ( + .clk(fc) + ); // generate the clock at any frequency + realtime half_cycle_fout; // time period to be set of the post vco clock + realtime fout_time; + always@(posedge fref) + begin + if (mes_done) begin // we can generate both the clocks as we have the reference period of the FREF + half_cycle_fout = vco_time*div; // No need to divide by two because we are using already the half cycle + if(half_cycle_fout != 0) + post_div_clk_gen.set_half_cycle(half_cycle_fout); // reset the time period of the post vco clock + end + end + assign fout_time = half_cycle_fout; + assign fout = rst_n==1'b1 ? fc:1'b0; +endmodule + + +interface clk_gen( + output bit clk +); + realtime half_cycle; + initial begin + half_cycle = 10; + clk = 0; + forever #half_cycle clk = ~clk; + end + function void set_half_cycle(input realtime c); + begin + if (half_cycle == 0) begin + $display("Error - can't set clock half_cycle to 0"); + end else begin + half_cycle = c; + end + end + endfunction +endinterface + + +module measure ( + input signal, + output time period, + output reg measured +); + reg last_time_valid = 0; + time last_time; + always @(posedge signal) begin + if (last_time_valid) begin + period = $time - last_time; + last_time_valid = 0; + measured <= 1'b1; + end else begin + last_time = $time; + last_time_valid = 1'b1; + measured <= 1'b0; + end + end +endmodule + +module PLL #( + parameter DIVIDE_CLK_IN_BY_2 = "FALSE", // Enable input divider (TRUE/FALSE) + parameter PLL_MULT = 16, // Clock multiplier value (16-1000) + parameter PLL_DIV = 1, // Clock divider value (1-63) + parameter CLK_OUT0_DIV = 2, // CLK_OUT0 divider value (2,3,4,5,6,7,8,10,12,16,20.24.32.40,48,64) + parameter CLK_OUT1_DIV = 2, // CLK_OUT1 divider value (2,3,4,5,6,7,8,10,12,16,20.24.32.40,48,64) + parameter CLK_OUT2_DIV = 2, // CLK_OUT2 divider value (2,3,4,5,6,7,8,10,12,16,20.24.32.40,48,64) + parameter CLK_OUT3_DIV = 2 // CLK_OUT3 divider value (2,3,4,5,6,7,8,10,12,16,20.24.32.40,48,64) +) ( + input PLL_EN, // PLL Enable + input CLK_IN, // Clock input + input CLK_OUT0_EN, // Enable CLK_OUT0 + input CLK_OUT1_EN, // Enable CLK_OUT1 + input CLK_OUT2_EN, // Enable CLK_OUT2 + input CLK_OUT3_EN, // Enable CLK_OUT3 + output CLK_OUT0, // CLK_OUT0 output + output CLK_OUT1, // CLK_OUT1 output + output CLK_OUT2, // CLK_OUT2 output + output CLK_OUT3, // CLK_OUT3 output + output GEARBOX_FAST_CLK, // Gearbox fast clock output + output LOCK // PLL lock signal +); + +time measured_fref_per; // time period of input clk FREF +logic pre_div_out; // output clk of the pre divider circuit +logic mes_done; // check signal when time period is calcuated it is set to high +realtime half_cycle_prediv; // time period of the initial clock +logic post_vco_out; // clock which s at the output of the vco +realtime half_cycle_vco; // time period to be set of the post vco clock +logic fc0, fc1,fc2,fc3; +realtime half_cycle_fout0,half_cycle_fout1,half_cycle_fout2,half_cycle_fout3; +int counter=0; +logic DELAY_LOCK =1'b0;// + +clk_gen pre_div_clk ( + .clk(pre_div_out) +); // start generating the clock at any frequency + +measure i_m1 ( + .signal(CLK_IN), + .period(measured_fref_per), + .measured(mes_done) + ); // measure the time period of the initial clock FREF + +always@(posedge CLK_IN) begin + if (mes_done && (DIVIDE_CLK_IN_BY_2=="TRUE")) + begin + half_cycle_prediv = (measured_fref_per*PLL_DIV); // The time period of the pre divider clock will be initial will be the time period of CLK_IN* DIV_CLK_IN + if(half_cycle_prediv != 0) + pre_div_clk.set_half_cycle(half_cycle_prediv); // Update the time period of predivider output clock. + end + else if (mes_done && (DIVIDE_CLK_IN_BY_2=="FALSE")) + begin + half_cycle_prediv = (measured_fref_per*PLL_DIV)/2; // The time period of the pre divider clock will be initial will be the time period of CLK_IN* DIV_CLK_IN + if(half_cycle_prediv != 0) + pre_div_clk.set_half_cycle(half_cycle_prediv); // Update the time period of predivider output clock. + end +end + +//VCO Generation +clk_gen post_vco_clk ( + .clk(post_vco_out) +); // generate the clock at any frequency + +always@(posedge CLK_IN) begin + if (mes_done) // we can generate both the clocks as we have the reference period of the CLK_IN + begin + half_cycle_vco = half_cycle_prediv/PLL_MULT; // No need to divide by two because we are using already the half cycle + if(half_cycle_vco != 0) + post_vco_clk.set_half_cycle(half_cycle_vco); // reset the time period of the post vco clock + end +end + +post_div #(.div(CLK_OUT0_DIV)) +pd0 +( + .fref(CLK_IN), + .mes_done(mes_done), + .rst_n(CLK_OUT0_EN), + .vco_time(half_cycle_vco), + .fout(CLK_OUT0) + //.fout_time(half_cycle_fout0) + ); +post_div #(.div(CLK_OUT1_DIV)) +pd1 +( + .fref(CLK_IN), + .mes_done(mes_done), + .rst_n(CLK_OUT1_EN), + .vco_time(half_cycle_vco), + .fout(CLK_OUT1) + //.fout_time(half_cycle_fout1) + ); +post_div #(.div(CLK_OUT2_DIV)) +pd2 +( + .fref(CLK_IN), + .mes_done(mes_done), + .rst_n(CLK_OUT2_EN), + .vco_time(half_cycle_vco), + .fout(CLK_OUT2) + //.fout_time(half_cycle_fout2) + ); +post_div #(.div(CLK_OUT3_DIV)) +pd3 +( + .fref(CLK_IN), + .mes_done(mes_done), + .rst_n(CLK_OUT3_EN), + .vco_time(half_cycle_vco), + .fout(CLK_OUT3) + //.fout_time(half_cycle_fout3) + ); + +// as per specification the lock is guaranted after the 2000 pfd (phase frequency detector) cycles. In the simulation model there is no pfd so a lock is set +// as we will get the output from the pre divider +always @(posedge CLK_IN) +begin + counter = counter + 1; + if (counter == PLL_DIV) + assign DELAY_LOCK = 1'b1; +end + +assign LOCK = ~PLL_EN ? 1'b0 : DELAY_LOCK; +assign GEARBOX_FAST_CLK = post_vco_out; + + initial begin + case(DIVIDE_CLK_IN_BY_2) + "TRUE" , + "FALSE": begin end + default: begin + $display("\nError: PLL instance %m has parameter DIVIDE_CLK_IN_BY_2 set to %s. Valid values are TRUE, FALSE\n", DIVIDE_CLK_IN_BY_2); + #1 $stop ; + end + endcase + + if ((PLL_MULT < 16) || (PLL_MULT > 1000)) begin + $display("PLL instance %m PLL_MULT set to incorrect value, %d. Values must be between 16 and 1000.", PLL_MULT); + #1 $stop; + end + + if ((PLL_DIV < 1) || (PLL_DIV > 63)) begin + $display("PLL instance %m PLL_DIV set to incorrect value, %d. Values must be between 1 and 63.", PLL_DIV); + #1 $stop; + end + case(CLK_OUT0_DIV) + 2 , + 3 , + 4 , + 5 , + 6 , + 8 , + 10 , + 12 , + 16 , + 20 , + 24 , + 32 , + 40 , + 48 , + 64: begin end + default: begin + $display("\nError: PLL instance %m has parameter CLK_OUT0_DIV set to %s. Valid values are 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 64\n", CLK_OUT0_DIV); + #1 $stop ; + end + endcase + case(CLK_OUT1_DIV) + 2 , + 3 , + 4 , + 5 , + 6 , + 8 , + 10 , + 12 , + 16 , + 20 , + 24 , + 32 , + 40 , + 48 , + 64: begin end + default: begin + $display("\nError: PLL instance %m has parameter CLK_OUT1_DIV set to %s. Valid values are 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 64\n", CLK_OUT1_DIV); + #1 $stop ; + end + endcase + case(CLK_OUT2_DIV) + 2 , + 3 , + 4 , + 5 , + 6 , + 8 , + 10 , + 12 , + 16 , + 20 , + 24 , + 32 , + 40 , + 48 , + 64: begin end + default: begin + $display("\nError: PLL instance %m has parameter CLK_OUT2_DIV set to %s. Valid values are 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 64\n", CLK_OUT2_DIV); + #1 $stop ; + end + endcase + case(CLK_OUT3_DIV) + 2 , + 3 , + 4 , + 5 , + 6 , + 8 , + 10 , + 12 , + 16 , + 20 , + 24 , + 32 , + 40 , + 48 , + 64: begin end + default: begin + $display("\nError: PLL instance %m has parameter CLK_OUT3_DIV set to %s. Valid values are 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 64\n", CLK_OUT3_DIV); + #1 $stop ; + end + endcase + + end + +endmodule +`endcelldefine