From 903a3c7b9b19496a3df690e253dee77ecb660e82 Mon Sep 17 00:00:00 2001 From: WangXuan95 <629708558@qq.com> Date: Sat, 3 Jun 2023 18:10:27 +0800 Subject: [PATCH] add support for byte-enable --- .gitignore | 6 + FTD2XX_guide.md | 108 ++ FTD3XX_guide.md | 113 ++ README.md | 998 ++++++++++++++++++ RTL/fpga_ft232h_example/clock_beat.v | 31 + RTL/fpga_ft232h_example/constraint_ft232h.sdc | 17 + .../fpga_top_ft232h_loopback.v | 115 ++ .../fpga_top_ft232h_rx_crc.v | 138 +++ .../fpga_top_ft232h_tx_mass.v | 136 +++ RTL/fpga_ft232h_example/rx_calc_crc.v | 91 ++ RTL/fpga_ft232h_example/tx_specified_len.v | 95 ++ RTL/fpga_ft600_example/clock_beat.v | 31 + RTL/fpga_ft600_example/constraint_ft600.sdc | 17 + .../fpga_top_ft600_loopback.v | 120 +++ .../fpga_top_ft600_rx_crc.v | 143 +++ .../fpga_top_ft600_tx_mass.v | 141 +++ RTL/fpga_ft600_example/rx_calc_crc.v | 91 ++ RTL/fpga_ft600_example/tx_specified_len.v | 95 ++ RTL/ftdi_245fifo/axi_stream_assert.v | 107 ++ RTL/ftdi_245fifo/axi_stream_downsizing.v | 82 ++ RTL/ftdi_245fifo/axi_stream_packing.v | 147 +++ RTL/ftdi_245fifo/axi_stream_resizing.v | 82 ++ RTL/ftdi_245fifo/axi_stream_upsizing.v | 127 +++ RTL/ftdi_245fifo/fifo2.v | 62 ++ RTL/ftdi_245fifo/fifo4.v | 98 ++ RTL/ftdi_245fifo/fifo_async.v | 97 ++ RTL/ftdi_245fifo/fifo_delay_submit.v | 85 ++ RTL/ftdi_245fifo/ftdi_245fifo_fsm.v | 129 +++ RTL/ftdi_245fifo/ftdi_245fifo_top.v | 675 ++++++++++++ RTL/ftdi_245fifo/resetn_sync.v | 26 + SIM/tb_ftdi_245fifo.v | 122 +++ SIM/tb_ftdi_245fifo_run_iverilog.bat | 5 + SIM/tb_ftdi_chip_model.v | 129 +++ figures/ft232h_driver_download.png | Bin 0 -> 10166 bytes figures/ft232h_example_sch.png | Bin 0 -> 33310 bytes figures/ft232h_program.png | Bin 0 -> 34156 bytes figures/ft232h_ready.png | Bin 0 -> 9532 bytes figures/ft600_driver_download.png | Bin 0 -> 11081 bytes figures/ft600_example_sch.png | Bin 0 -> 37378 bytes figures/ft600_ready.png | Bin 0 -> 9363 bytes figures/ports.png | Bin 0 -> 32946 bytes figures/structure.png | Bin 0 -> 31952 bytes python/USB_FTX232H_FT60X.py | 283 +++++ python/usb_loopback_mass.py | 57 + python/usb_loopback_simple.py | 33 + python/usb_rx_mass.py | 55 + python/usb_tx_crc.py | 88 ++ 47 files changed, 4975 insertions(+) create mode 100644 .gitignore create mode 100644 FTD2XX_guide.md create mode 100644 FTD3XX_guide.md create mode 100644 README.md create mode 100644 RTL/fpga_ft232h_example/clock_beat.v create mode 100644 RTL/fpga_ft232h_example/constraint_ft232h.sdc create mode 100644 RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v create mode 100644 RTL/fpga_ft232h_example/fpga_top_ft232h_rx_crc.v create mode 100644 RTL/fpga_ft232h_example/fpga_top_ft232h_tx_mass.v create mode 100644 RTL/fpga_ft232h_example/rx_calc_crc.v create mode 100644 RTL/fpga_ft232h_example/tx_specified_len.v create mode 100644 RTL/fpga_ft600_example/clock_beat.v create mode 100644 RTL/fpga_ft600_example/constraint_ft600.sdc create mode 100644 RTL/fpga_ft600_example/fpga_top_ft600_loopback.v create mode 100644 RTL/fpga_ft600_example/fpga_top_ft600_rx_crc.v create mode 100644 RTL/fpga_ft600_example/fpga_top_ft600_tx_mass.v create mode 100644 RTL/fpga_ft600_example/rx_calc_crc.v create mode 100644 RTL/fpga_ft600_example/tx_specified_len.v create mode 100644 RTL/ftdi_245fifo/axi_stream_assert.v create mode 100644 RTL/ftdi_245fifo/axi_stream_downsizing.v create mode 100644 RTL/ftdi_245fifo/axi_stream_packing.v create mode 100644 RTL/ftdi_245fifo/axi_stream_resizing.v create mode 100644 RTL/ftdi_245fifo/axi_stream_upsizing.v create mode 100644 RTL/ftdi_245fifo/fifo2.v create mode 100644 RTL/ftdi_245fifo/fifo4.v create mode 100644 RTL/ftdi_245fifo/fifo_async.v create mode 100644 RTL/ftdi_245fifo/fifo_delay_submit.v create mode 100644 RTL/ftdi_245fifo/ftdi_245fifo_fsm.v create mode 100644 RTL/ftdi_245fifo/ftdi_245fifo_top.v create mode 100644 RTL/ftdi_245fifo/resetn_sync.v create mode 100644 SIM/tb_ftdi_245fifo.v create mode 100644 SIM/tb_ftdi_245fifo_run_iverilog.bat create mode 100644 SIM/tb_ftdi_chip_model.v create mode 100644 figures/ft232h_driver_download.png create mode 100644 figures/ft232h_example_sch.png create mode 100644 figures/ft232h_program.png create mode 100644 figures/ft232h_ready.png create mode 100644 figures/ft600_driver_download.png create mode 100644 figures/ft600_example_sch.png create mode 100644 figures/ft600_ready.png create mode 100644 figures/ports.png create mode 100644 figures/structure.png create mode 100644 python/USB_FTX232H_FT60X.py create mode 100644 python/usb_loopback_mass.py create mode 100644 python/usb_loopback_simple.py create mode 100644 python/usb_rx_mass.py create mode 100644 python/usb_tx_crc.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..099dbfb --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ + +FT232H_UniFPGA +FT600_FPGArduino +vivado + +**/__pycache__ diff --git a/FTD2XX_guide.md b/FTD2XX_guide.md new file mode 100644 index 0000000..2ebd9a1 --- /dev/null +++ b/FTD2XX_guide.md @@ -0,0 +1,108 @@ +[English](#en) | [中文](#cn) + +  + +Install FTD2XX Driver and Python ftd2xx Library +==================================== + +To run FT232H related Python programs on Windows, follow these steps: + +> Note: This document was written in 2019. If the official website is updated later, the general process will definitely remain unchanged, but some operational details (for example, the download file cannot be found on the official website) will need to be worked around. + +### Step1: Install FTD2XX Driver and download FTD2XX.dll + +Go to [D2XX Driver website](https://www.ftdichip.com/Drivers/D2XX.htm), in the table in the D2XX Drivers column, download the driver (.exe file) and install it. As shown below. + +Also, to download the DLL, unzip it and find the FTD2XX.dll file that matches your computer. For a 32-bit computer, find the 32-bit(i386) DLL; for a 64-bit computer, find the 64-bit(amd64) DLL. If the file name is FTD2XX64.DLL, etc., please always rename it to FTD2XX.DLL + +![FT232h驱动下载](./figures/ft232h_driver_download.png) + +### Step2: Verify Installation + +Insert the FT232H USB port of the development board into the computer. If the driver is installed successfully, a **USB Serial Converter** should be identified in the Windows Device Manager. As shown below. + +![FT232H被识别](./figures/ft232h_ready.png) + +### Step3: Install Python3 + +If you don't have Python3 installed, please go to [Anaconda website](https://www.anaconda.com/products/individual) to download and install Python3, the major version number must be Python3, not Python2. + +Note: If it is a 32-bit computer, please install 32-bit Python; if it is a 64-bit computer, please install 64-bit Python. + +### Step4: Install ftd2xx Library for Python + +Open CMD or PowerShell, run command: + +```powershell +python -m pip install ftd2xx +``` + +### Step5: Copy FTD2XX.dll File to Python Environment + +Copy the FTD2XX.DLL file we downloaded in step1 to the Python's root directory (for example, on my computer, the Python root directory is **C:/Anaconda3/** ). Note that 32-bit Python must correspond to a 32-bit DLL; 64-bit Python must correspond to a 64-bit DLL. + +Then, you can run the following statement in python to verify its installation: + +```python +import ftd2xx +``` + + +Now the Python runtime environment required by the FT232H is ready. + + +  + +  + +  + +  + +安装 FTD2XX 驱动和 Python FTD2XX 库 +==================================== + +要在 Windows 上运行 FT232H 相关的 Python 程序,请进行以下步骤: + +> :warning: 该文档写于 2019 年,如果之后官网更新,大致流程肯定不变,但一些操作细节(例如官网上找不到下载文件了)就要变通变通了。 + +### 步骤1:安装 FTD2XX 驱动和 FTD2XX.DLL + +进入 [D2XX Driver 官网页面](https://www.ftdichip.com/Drivers/D2XX.htm) ,在 D2XX Drivers 那一栏的表格里,下载exe形式的驱动并安装。如下图。 + +另外,要下载 DLL 压缩包, 解压后在里面找到符合你计算机的 FTD2XX.DLL 文件。若为32位计算机,请找到 32-bit(i386) DLL;若为64位计算机,请找到 64-bit (amd64) DLL。如果文件名是 FTD2XX64.DLL 等,请一律重命名为 FTD2XX.DLL + +![FT232h驱动下载](./figures/ft232h_driver_download.png) + +### 步骤2:验证驱动安装 + +将开发板的 FT232H USB 口插入电脑,如果驱动安装成功,则 Windows 设备管理器里应该识别出 **USB Serial Converter** 。如下图。 + +![FT232H被识别](./figures/ft232h_ready.png) + +### 步骤3:安装 Python3 + +如果你没有安装 Python3, 请前往 [Anaconda官网](https://www.anaconda.com/products/individual) 下载安装 Python3 ,必须是 Python3 ,不能是 Python2 。 + +注:若为32位计算机,请安装32位的Python;若为64位计算机,请安装64位的Python。 + +### 步骤4:安装 python ftd2xx 库 + +打开 CMD 或 PowerShell ,运行命令: + +```powershell +python -m pip install ftd2xx +``` + +### 步骤5:复制 FTD2XX.DLL 文件到 Python 环境中 + +复制 **步骤1** 中我们找到的 FTD2XX.DLL 文件到 Python 根目录(例如在我的电脑上, Python 根目录是 **C:/Anaconda3/** )。注意32位的Python必须对应32位的DLL;64位的Python必须对应64位的DLL。 + +然后,可以在 python 中运行以下语句来验证安装: + +```python +import ftd2xx +``` + + +至此,FT232H 所需的 Python 运行环境已就绪。 diff --git a/FTD3XX_guide.md b/FTD3XX_guide.md new file mode 100644 index 0000000..ae79697 --- /dev/null +++ b/FTD3XX_guide.md @@ -0,0 +1,113 @@ +[English](#en) | [中文](#cn) + +  + +Install FTD3XX Driver and Python ftd3xx Library +==================================== + +To run FT600 related Python programs on Windows, follow these steps: + +> Note: This document was written in 2019. If the official website is updated later, the general process will definitely remain unchanged, but some operational details (for example, the download file cannot be found on the official website) will need to be worked around. + +### Step1: Install FTD3XX Driver and download FTD3XX.dll + +Go to [D3XX Driver webpage](https://www.ftdichip.com/Drivers/D3XX.htm) , in the table in the D3XX Drivers column, download the driver (.exe file) and install it. As shown below. + +Also, to download the DLL, unzip it and find the FTD3XX.dll file that matches your computer. For a 32-bit computer, find the 32-bit(i386) DLL; for a 64-bit computer, find the 64-bit(amd64) DLL. If the file name is FTD3XX64.DLL, etc., please always rename it to FTD3XX.DLL + +![FT600驱动下载](./figures/ft600_driver_download.png) + + + +### Step2: Verify Installation + +Insert the FT600 USB port of the development board into the computer. If the driver is installed successfully, a **FTDI FT600 USB 3.0 Bridge Device** should be identified in the Windows Device Manager. As shown below. + +![FT600被识别](./figures/ft600_ready.png) + +### Step3: Install Python3 + +If you don't have Python3 installed, please go to [Anaconda official website](https://www.anaconda.com/products/individual) to download and install Python3, the major version number must be Python3, not Python2. + +Note: If it is a 32-bit computer, please install 32-bit Python; if it is a 64-bit computer, please install 64-bit Python. + +### Step4: Install ftd3xx Library for Python + +Open CMD or PowerShell, run command: + +```powershell +python -m pip install ftd3xx +``` + +~~ftd3xx library doesn't seem to be able to be installed with the pip install command. Please open the http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm webpage, there is Python support at the bottom.Download and unzip it, find **setup.py** in it, and run the CMD command pytho n setup.py install to install it.~~ + +### Step5: Copy FTD3XX.dll File to Python Environment + +Copy the FTD2XX.DLL file we downloaded in step1 to the Python's root directory (for example, on my computer, the Python root directory is **C:/Anaconda3/** ). Note that 32-bit Python must correspond to a 32-bit DLL; 64-bit Python must correspond to a 64-bit DLL. + +Then, you can run the following statement in python to verify its installation: + +```python +import ftd3xx +``` + +Now the Python runtime environment required by the FT600 is ready. + +  + +  + +  + +  + +安装 FTD3XX 驱动和 Python FTD3XX 库 +==================================== + +要在 Windows 上运行 FT600 相关的 Python 程序,请进行以下步骤: + +> 注:该文档写于 2019 年,如果之后官网更新,大致流程肯定不变,但一些操作细节(例如官网上找不到下载文件了)就要变通变通了。 + +### 步骤1:准备 D3XX 驱动 和 FTD3XX.DLL + +进入 [D3XX Driver 官网页面](https://www.ftdichip.com/Drivers/D3XX.htm) ,在 D3XX Drivers 那一栏的表格里。下载exe形式的驱动并安装。如下图。 + +另外,要下载 DLL 压缩包, 解压后在里面找到符合你计算机的 FTD3XX.DLL 文件。若为32位计算机,请找到 32-bit(i386) DLL;若为64位计算机,请找到 64-bit (amd64) DLL。如果文件名是 FTD3XX64.DLL 等,请一律重命名为 FTD3XX.DLL + +![FT600驱动下载](./figures/ft600_driver_download.png) + +  + +### 步骤2:验证驱动安装 + +将开发板的 FT600 USB 口插入电脑,如果驱动安装成功,则 Windows 设备管理器里应该识别出 **FTDI FT600 USB 3.0 Bridge Device** 。 + +![FT600被识别](./figures/ft600_ready.png) + +### 步骤3:安装 python3 + +如果你没有安装 Python3, 请前往 [Anaconda官网](https://www.anaconda.com/products/individual) 下载安装 Python3 ,必须是 Python3 ,而不是 Python2。 + +注:若为32位计算机,请安装32位的Python;若为64位计算机,请安装64位的Python。 + +### 步骤4:安装 Python ftd3xx 库 + +打开 CMD 或 Powershell ,运行: + +``` +python -m pip install ftd3xx +``` + +~~ftd3xx似乎无法用pip install命令来安装。请打开http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm网页, 网页最下方有 Python 的支持。下载后解压,在里面找到setup.py,在本目录中运行 CMD 命令python setup.py install来安装。~~ + +### 步骤5:复制 FTD3XX.DLL 文件到 Python 环境中 + +复制步骤1中我们找到的 FTD3XX.DLL 文件到 Python 根目录(例如在我的电脑上, Python 根目录是 **C:/Anaconda3/** )。注意32位的Python必须对应32位的DLL;64位的Python必须对应64位的DLL。 + +然后,可以在 python 中运行以下语句来验证安装: + +```python +import ftd3xx +``` + +至此,FT600 所需的 Python 运行环境已就绪。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..314afa9 --- /dev/null +++ b/README.md @@ -0,0 +1,998 @@ +![语言](https://img.shields.io/badge/语言-verilog_(IEEE1364_2001)-9A90FD.svg) ![仿真](https://img.shields.io/badge/仿真-iverilog-green.svg) ![部署](https://img.shields.io/badge/部署-quartus-blue.svg) ![部署](https://img.shields.io/badge/部署-vivado-FF1010.svg) + +[English](#en) | [中文](#cn) + +  + +FTDI 245fifo controller +=========================== + +An FPGA-based controller for [FT232H](https://ftdichip.com/Products/ICs/FT232H.htm) , [FT2232H](https://ftdichip.com/Products/ICs/FT2232H.htm) , [FT600](https://ftdichip.com/Products/ICs/FT600.html) , [FT601](https://ftdichip.com/Products/ICs/FT600.html) chips in sync-245-fifo mode, which can realize fast USB communication between FPGA and Host-PC. + +> Update on June 2023: Add support for byte enable signals. Now it supports all features of FTDI sync-245-fifo mode, and the length of the sending and receiving bytes no longer needs to be a multiple of FTDI chip's byte-width. + +  + +# Introduction + +**sync-245-fifo mode** is the highest speed transmission mode of FTDI's USB series chips. This repository implement a sync-245-fifo mode controller Verilog IP core (**ftdi_245fifo_top**), providing AXI-stream send and receive interfaces for Verilog developers. + +In addition, this repository provides supplementary: +* Simulation code to verify **ftdi_245fifo_top** +* FPGA sample code as a demo of using **ftdi_245fifo_top** + +For Host-PC, I provide: + +* FTDI driver installation tutorials, +* Python3 FTDI USB software library (ftd2xx and ftd3xx) installation tutorials, +* Several Python3 programs for testing. + +> Note: Although the example code of Host-PC is written in Python (I used to use Python), you can also write program in other languages (C++, C #, etc.) to communicate with the FTDI chip. The design on FPGA is not limited to which programming language you use on Host-PC. + +  + +| ![module_structure](./figures/structure.png) | +| :------------------------------------------: | +| **Figure1** : diagram of ftdi_245fifo_top.v | + +  + +## IP's Technical Features + +* Pure Verilog design, which is easy to transplant to FPGAs of Altera, Xilinx, or other manufacturers. +* Standard AXI-stream interface for sending and receiving. +* **Send/Receive scheduling**: The interface between FTDI USB chip and FPGA is half-duplex. This module schedules the sending and receiving using time-division multiplexing to achieve **independent sending and receiving**. +* **Clock Domain Crossing**: The FTDI USB chip has its own clock. This module uses asynchronous FIFO to realize clock domain crossing, so that the send and receive AXI-stream interface can use **custom clocks** . +* **Bit width conversion**: This module realizes the bit width conversion so that the **bit width of the AXI-stream can be customized** . + +  + +## Performance Test Results + +| | FT232H / FT2232H | FT600 | FT601 | +| :-------------------------: | :--------------: | :-------: | :----------: | +| USB generation | USB2.0 HS | USB3.0 SS | USB3.0 SS | +| Theoretical Throughput | 60MB/s | 200MB/s | 400MBps | +| Tested Throughput (FPGA→PC) | 42MB/s | 140MB/s | not test yet | +| Tested Throughput (PC→FPGA) | 35MB/s | 175MB/s | not test yet | + +  + +  + +# ftdi_245fifo Module Manual + +The top design code is in [ftdi_245fifo_top.v](./RTL/ftdi_245fifo/ftdi_245fifo_top.v) in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) folder, which can be called by FPGA developers to develop their own USB communication applications. Its interface and parameters are shown in **Figure2**. + +| ![module_ports](./figures/ports.png) | +| :----------------------------------------: | +| **Figure2** : interfaces of ftdi_245fifo.v | + +  + +## Module Parameters + +To use this module, you should first determine the value of each Verilog parameter, as shown in the following table: + +| parameter | Introduction | +| ----------- | ------------------------------------------------------------ | +| `TX_EW` | Determines the data width of the AXI-stream sending interface (that is, the width of `tx_data`): 0 corresponds to 8bit width, 1 corresponds to 16bit width, 2 corresponds to 32bit width, and so on. It can be set arbitrarily according to your requirement and is not limited by the FTDI USB chip you used. | +| `TX_EA` | Determines the depth of send buffer, depth=2^TX_EA. The default is 10 (that is, the default depth is 1024). If the FPGA BRAM is larger, it can be set larger to improve burst performance. | +| `RX_EW` | Determines the data width of the AXI-stream receiving interface (that is, the width of `rx_data`): 0 corresponds to 8bit width, 1 corresponds to 16bit width, 2 corresponds to 32bit width, and so on. It can be set arbitrarily according to your requirement and is not limited by the FTDI USB chip you used. | +| `RX_EA` | Determines the depth of receive buffer, depth=2^RX_EA. The default is 10 (that is, the default depth is 1024). If the FPGA BRAM is larger, this item can be set larger to improve burst performance. | +| `CHIP_TYPE` | should be `"FTx232H"` , `"FT600"` , or `"FT601"` according to the FTDI chip you used | + +  + +## Module Interface: reset + +The reset signal of ftdi_245fifo_top.v is: + +```verilog +input wire rstn_async; +``` + +`rstn_async` is the global asynchronous reset of the module, 0 means reset, 1 means release reset. This signal can be directly set to `1'b1`, or connected to the global reset signal. + +  + +## Module Interface: Connect to FTDI Chip + +The FTDI chip's signals of ftdi_245fifo_top.v are: + +```verilog +// Connect to FTDI chip ------------------------------ +input wire ftdi_clk; +input wire ftdi_rxf_n; +input wire ftdi_txe_n; +output wire ftdi_oe_n; +output wire ftdi_rd_n; +output wire ftdi_wr_n; +inout [(8< :warning: FTDI chips can only send and receive pure data streams without the concept of data packets, so `tx_tlast` is only used to control whether to send immediately. After the data is sent to the computer, the software cannot know where is the boundary that `tx_tlast` specified. + +  + +## Module Interface: AXI-stream receiving interface + +The receiving interface is a standard AXI-stream master, which allows users to receive data from FTDI chip (from Host-PC). Corresponding definations: + +```verilog +// AXI-stream master receiving interface ------------------------------ +input wire rx_clk; // AXI-stream clock +input wire rx_tready; +output wire rx_tvalid; +output wire [(8< :warning: FTDI chips can only send and receive pure data streams without the concept of data packets, so `rx_tlast` is only used to indicate whether the current data is the last data taken out of the FTDI chip's buffer. The software on the Host-PC cannot control `rx_tlast` signal. For example, if you call `usb.send()` function twice in the software, the two blocks of data may be merged after being sent to the FPGA, and there may not be a `rx_tlast=1` boundary between them. + +  + +## Further explanation for tvalid&tready handshake + +Take the following waveform as an example, in this waveform, the user interface sends 3 data: D1, D2, D3. + +* In the 1st and 2nd cycles, the user sets `tx_tvalid=0` , so it is temporarily idle and no data is sent. + +* In the 3rd cycle, the user wants to send D1, so it set `tx_tvalid=1`, this cycle `tx_tready=1`, indicating that D1 is sent to buffer successfully. + +* In the 4th, 5th, 6th, and 7th cycles, the user wants to send D2, so it set `tx_tvalid=1` , but the 4th, 5th, 6th cycles `tx_ready=0` cause the transmission to temporarily fail until the 7th cycle `tx_tready=1 ` is sent successfully. + +* In the 8th and 9th cycles, the user sets `tx_tvalid=0`, so it is temporarily idle and no data is sent. + +* In the 10th cycle, the user wants to send D3, so it set `tx_tvalid=1`, this cycle `tx_tready=1`, indicating that D3 is sent successfully. + +``` +cycle 1 2 3 4 5 6 7 8 9 10 11 + _ __ __ __ __ __ __ __ __ __ __ __ + clk \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \ + _____________________________ _____ +tx_tvalid ___________/ \___________/ \________ + _________________ ________________________________ +tx_tready \_________________/ + _____ _______________________ _____ +tx_tdata/tx_tkeep/tx_tlast XXXXXXXXXXXX__D1_X___________D2__________XXXXXXXXXXXXX__D3_XXXXXXXXX +``` + +  + +  + +Simulation +============================= + +Simulation related files are in the [SIM](./SIM) folder, where: + +- tb_ftdi_chip_model.v simulates a simple behavior of FTDI chip. +- tb_ftdi_245fifo.v is the top module for simulation, which connect tb_ftdi_chip_model.v to ftdi_ft245fifo_top (design under test). +- tb_ftdi_245fifo_run_iverilog.bat is the command script to run the iverilog simulation (for Windows). + +The behavior of this simulation is that the FTDI chip continuously sends increasing bytes to the FPGA. The FPGA connects the AXI-stream sending interface and receiving interface loopback. Therefore, the data will be sent back to the FTDI chip, and the FTDI chip will receive the increasing bytes. + +## Use iverilog to simulate + +Before using iverilog for simulation, you need to install iverilog , see: [iverilog_usage](https://github.com/WangXuan95/WangXuan95/blob/main/iverilog_usage/iverilog_usage.md) + +Then double-click tb_ftdi_245fifo_run_iverilog.bat to run simulation (only for Windows), and then you can open the generated dump.vcd file to view the waveform. + +## Use other simulators + +You need to add all .v files in [SIM](./SIM) folder and [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) folder into the simulation project. And set tb_ftdi_245fifo.v as the top. And then run simulation. + +  + +  + +# Getting started with FT232H + +FT232H is a USB2.0 High Speed communication chip with a theoretical rate of 60MBps. In this demo, the FPGA communicates with Host-PC through FT232H. + +> :warning: This demo also applies to FT2232H (you need to work around it yourself), because FT2232H is highly similar to FT232H. Note that FT2232H's **channel-A** can be configured to **sync-245-fifo mode** , but **channel-B** cannot. +> + +## Step1: Install FTD2XX Driver and Library + +Please follow the instructions in [FTD2XX_guide.md](./FTD2XX_guide.md) to install the FTD2XX driver and Python FTD2XX library on your Host-PC (Windows system). + +## Step2: Deploy FPGA Project + +Following is the hierarchical structure of the FPGA project: + +- **fpga_top_ft232h_loopback.v** (in [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) ) , is the top of the whole project + - **clock_beat.v** (in [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) ) + - **ftdi_245fifo_top.v** (in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) ) , is the top of IP + - Other .v files in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) , they will be called by **ftdi_245fifo_top** + +Please add above-mentioned files to FPGA project + +Then, please make pin constraints in the FPGA project. Specifically, you need to check the schematic of your FPGA board (or the supporting materials of the development board) to understand which FT232H pin should connected to which FPGA pin; then, constraint the signals of the top layer of the FPGA project to the corresponding FPGA pin. The correspondence is shown in the following table (this table is simplified from [FT232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf) page 9): + +| FT232H pin | FT232H pin name | FT232H pin name (in 245-fifo mode) | signal name in top module | +| :--------: | :-------------: | :--------------------------------: | :-----------------------: | +| 13 | ADBUS0 | D0 | ftdi_data[0] | +| 14 | ADBUS1 | D1 | ftdi_data[1] | +| 15 | ADBUS2 | D2 | ftdi_data[2] | +| 16 | ADBUS3 | D3 | ftdi_data[3] | +| 17 | ADBUS4 | D4 | ftdi_data[4] | +| 18 | ADBUS5 | D5 | ftdi_data[5] | +| 19 | ADBUS6 | D6 | ftdi_data[6] | +| 20 | ADBUS7 | D7 | ftdi_data[7] | +| 21 | ACBUS0 | RXF\# | ftdi_rxf_n | +| 25 | ACBUS1 | TXE\# | ftdi_txe_n | +| 26 | ACBUS2 | RD\# | ftdi_rd_n | +| 27 | ACBUS3 | WR\# | ftdi_wr_n | +| 28 | ACBUS4 | SIWU\# | ftdi_siwu (=1) :warning: | +| 29 | ACBUS5 | CLKOUT | ftdi_clk | +| 30 | ACBUS6 | OE\# | ftdi_oe_n | +| 31 | ACBUS7 | PWRSAV\# | ftdi_pwrsav (=1) :warning: | +| 34 | RESET\# | RESET\# | ftdi_resetn (=1) :warning: | + +> :warning: SIWU\#, PWRSAV\# and RESET\# in the above table are actually always pulled to high. Some boards directly use resistors to pull-up them without connecting them to the FPGA, in this case, you don't need to constrain these pins and simply comment out the statements related to them in fpga_top_example_ft232h.v. + +  + +If you want to design the FT232H PCB by yourself, please refer to **Figure3**. + +| ![ft232h_example_sch](./figures/ft232h_example_sch.png) | +| :----------------------------------------------------------: | +| **Figure3** : Example schematic of FT232H USB interface circuit. | + +  + +Notations of the clock and timing constraints: + +- There are two clocks in this project: the main clock (clk) and the clock from the FT232H chip (ftdi_clk). +- For FT232H, ftdi_clk frequency is 60MHz. +- There's no special limitations on the frequency of clk. which can be from a few MHz to a few hundred MHz. The simplest way is to directly use the oscillator of FPGA board. +- `constraint_ft232h.sdc` shows an example of timing constraints, which constrain: + - Frequency of ftdi_clk (60MHz) + - Frequency of clk (you need to constrain it to the actual frequency) + - Specify that ftdi_clk and clk are asynchronous. + +- `constraint_ft232h.sdc` is directly applicable to Altera Quartus projects. For Xilinx's xdc, the constraint statements should be similar (there may be some slight differences, please modify them yourself) + +- If you are lazy to make timing constraints, you can also leave them unconstrained, it is probably no problem. + +  + +Other Notations: + +- LED[3:0] connects 4 LEDs (not necessary) , where: + - if ftdi_clk runs, LED[3] will blink. Usually ftdi_clk will start to run when first time of communication rather than when power on. + - LED[2:0] shows the low 3 bits of the last-received data. +- parameter `CHIP_TYPE` of ftdi_245fifo_top is set to "FTx232H", because we are using FT232H / FT2232H chip. + +  + +## Step3: Program FT232H chip + +For each FT232H chip, it needs to be programmed to 245-fifo mode when it is used for the first time. Each FT232H chip only needs to be programmed once, because there is an EEPROM chip on the periphery of the FT232H to permanently save the configuration, and it does not need to be programmed again every time it is used (unless you program it to another mode). + +First go to the [FTDI download page](https://ftdichip.com/utilities/#ft_prog) to download and install **FT\_Prog** software. Then do the following steps: + +* Plug the USB of FT232H to the computer, and unplug all other FTDI USB chip or device (including FPGA downloaders, because some FPGA downloaders, such as Xilinx Digilent downloaders, are implemented using FT2232H chip, if the program inside the downloader is overwritten, your downloader will be broken). +* Open **FT\_Prog** software. +* As shown in **Figure4**, click **Scan and Parse** (the small magnifying glass) to scan all FTDI chips plugged into the computer and find the corresponding chip of FT232H. +* In the property tree below FT232H, expand level by level, find and click "Hardware" . +* Select 245 FIFO mode on the right. +* Click **Program** (the small lightning) in the upper toolbar. +* A confirmation window pops up, click Program. Program to FT232H. +* After programming, you need to re-plug the USB to let this configuration takes effect. + +| ![ft232h_program](./figures/ft232h_program.png) | +| :-------------------------------------------------: | +| **Figure4** : Program FT232H chip to 245-fifo mode. | + +  + +## Step4: Run Programs on Host-PC + +Before running these programs, make sure that FT232H has been programmed into 245-fifo mode according to step3, and the FPGA project created in step2 has been programed to FPGA. + +I provide the following Python programs in [python](./python) folder that will communicate with FPGA through FT232H. + +| File Name | Function | +| ----------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | Defines the USB_FTX232H_FT60X_sync245mode class, implements constructor, close, send, recv methods. This class is universal for FT232H, FT2232H, FT600, and FT601. | +| usb_loopback_simple.py | The Host-PC firstly sends 16 bytes to FPGA and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these 16 bytes. This program is associated with the FPGA top design fpga_top_ft232h_loopback.v | +| usb_loopback_mass.py | The Host-PC firstly sends a large amount of data to FPGA, and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these bytes and the program will compare whether the received data is consistent with the previously sent data to verify the correctness of the loopback. This program is associated with the FPGA top design fpga_top_ft232h_loopback.v | + +  + +  + +# Getting Started with FT600 + +The FT600 is a USB3.0 Super Speed communication chip with a theoretical rate of 200MBps. In this example, the FPGA communicates with the Host-PC through FT600. + +## Step1: Install FTD3XX Driver and Library + +Please follow the instructions in [FTD3XX_guide.md](./FTD3XX_guide.md) to install the FTD3XX driver and Python FTD3XX library on your Host-PC (Windows system). + +## Step2: Deploy FPGA Project + +Following is the hierarchical structure of the FPGA project: + +- **fpga_top_ft600_loopback.v** (in [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) ) , is the top of the whole project + - **clock_beat.v** (in [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) ) + - **ftdi_245fifo_top.v** (in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) ) , is the top of IP + - Other .v files in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) , they will be called by **ftdi_245fifo_top** + +Please add above-mentioned files to FPGA project. + +Then, please make pin constraints in the FPGA project. Specifically, you need to check the schematic of your FPGA board (or the supporting materials of the development board) to understand which FT600 pin should connected to which FPGA pin; then, constraint the signals of the top layer of the FPGA project (that is, fpga_top_example_ft600.v) to the corresponding FPGA pin. The correspondence is shown in the following table (this table is simplified from [FT600 DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT600Q-FT601Q%20IC%20Datasheet.pdf) page 7-10): + +| FT600 pin number | FT600 pin name | signal name in top module | +| :--------------: | :------------: | :----------------------------------------: | +| 33 | DATA_0 | ftdi_data[0] | +| 34 | DATA_1 | ftdi_data[1] | +| 35 | DATA_2 | ftdi_data[2] | +| 36 | DATA_3 | ftdi_data[3] | +| 39 | DATA_4 | ftdi_data[4] | +| 40 | DATA_5 | ftdi_data[5] | +| 41 | DATA_6 | ftdi_data[6] | +| 42 | DATA_7 | ftdi_data[7] | +| 45 | DATA_8 | ftdi_data[8] | +| 46 | DATA_9 | ftdi_data[9] | +| 47 | DATA_10 | ftdi_data[10] | +| 48 | DATA_11 | ftdi_data[11] | +| 53 | DATA_12 | ftdi_data[12] | +| 54 | DATA_13 | ftdi_data[13] | +| 55 | DATA_14 | ftdi_data[14] | +| 56 | DATA_15 | ftdi_data[15] | +| 2 | BE_0 | ftdi_be[0] | +| 3 | BE_1 | ftdi_be[1] | +| 43 | CLKOUT | ftdi_clk | +| 5 | RXF_N | ftdi_rxf | +| 4 | TXE_N | ftdi_txe | +| 9 | OE_N | ftdi_oe | +| 8 | RD_N | ftdi_rd | +| 7 | WR_N | ftdi_wr | +| 6 | SIWU_N | ftdi_siwu (=1) :warning: | +| 10 | RESET_N | ftdi_resetn (=1) :warning: | +| 11 | WAKEUP_N | ftdi_wakeupn (=0) :triangular_flag_on_post: | +| 12 | GPIO0 | ftdi_gpio0 (=0) :triangular_flag_on_post: | +| 13 | GPIO1 | ftdi_gpio1 (=0) :triangular_flag_on_post: | + +> :warning: SIWU\_N and RESET\_N in the above table are actually always pulled to high. Some boards directly use resistors to pull-up them without connecting them to the FPGA, in this case, you don't need to constrain these pins and simply comment out the statements related to them in fpga_top_example_ft600.v. +> +> :triangular_flag_on_post: WAKEUP\_N, GPIO0 and GPIO1 in the above table are actually always pulled to GND. Some boards directly use resistors to pull-down them without connecting them to the FPGA, in this case, you don't need to constrain these pins and simply comment out the statements related to them in fpga_top_example_ft600.v. + +If you want to draw FT600 PCB by yourself, please refer to **Figure5**. + +| ![ft600_example_sch](./figures/ft600_example_sch.png) | +| :----------------------------------------------------------: | +| **Figure3** : Example schematic of FT600 USB interface circuit. | + +  + +Notations of the clock and timing constraints: + +- There are two clocks in this project: the main clock (clk) and the clock from the FT600 chip (ftdi_clk). +- For FT600, ftdi_clk frequency is 100MHz. +- There's no special limitations on the frequency of clk. which can be from a few MHz to a few hundred MHz. The simplest way is to directly use the oscillator of FPGA board. +- `constraint_ft600.sdc` shows an example of timing constraints, which constrain: + - Frequency of ftdi_clk (100MHz) + - Frequency of clk (you need to constrain it to the actual frequency) + - Specify that ftdi_clk and clk are asynchronous. + +- `constraint_ft600.sdc` is directly applicable to Altera Quartus projects. For Xilinx's xdc, the constraint statements should be similar (there may be some slight differences, please modify them yourself) + +- If you are lazy to make timing constraints, you can also leave them unconstrained, it is probably no problem. + +  + +Other Notations: + +- LED[3:0] connects 4 LEDs (not necessary) , where: + - if ftdi_clk runs, LED[3] will blink. Usually ftdi_clk will start to run when first time of communication rather than when power on. + - LED[2:0] shows the low 3 bits of the last-received data. +- parameter `CHIP_TYPE` of ftdi_245fifo_top is set to "FT600", because we are using FT600 chip. + +  + +## Step3: Run Programs on Host-PC + +Before running these programs, make sure that the FPGA project created in step2 has been programed to FPGA. + +I provide the following Python programs in [python](./python) folder that will communicate with FPGA through FT600. + +| File Name | Function | +| ---------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | Defines the USB_FTX232H_FT60X_sync245mode class, implements constructor, close, send, recv methods. This class is universal for FT232H, FT2232H, FT600, and FT601. | +| usb_loopback_simple.py | The Host-PC firstly sends 16 bytes to FPGA and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these 16 bytes. This program is associated with the FPGA top design fpga_top_ft600_loopback.v | +| usb_loopback_mass.py | The Host-PC firstly sends a large amount of data to FPGA, and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these bytes and the program will compare whether the received data is consistent with the previously sent data to verify the correctness of the loopback. This program is associated with the FPGA top design fpga_top_ft600_loopback.v | + +  + +# Reference + +* FT232H chip:http://www.ftdichip.cn/Products/ICs/FT232H.htm +* FT232H software example:http://www.ftdichip.cn/Support/SoftwareExamples/CodeExamples.htm +* FT600 chip:http://www.ftdichip.cn/Products/ICs/FT600.html +* FT600/FT601 software example: http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm + + +  + +  + +  + +  + + +FTDI 245fifo controller +=========================== + +[FT232H](https://ftdichip.com/Products/ICs/FT232H.htm)、[FT2232H](https://ftdichip.com/Products/ICs/FT2232H.htm)、[FT600](https://ftdichip.com/Products/ICs/FT600.html) 、 [FT601](https://ftdichip.com/Products/ICs/FT600.html) 等芯片在 sync-245-fifo 模式下的控制器,实现 FPGA 与上位机 (电脑) 的高速通信。 + +> 2023/6 更新内容:加入 byte enable 信号的支持。现在已支持 FTDI sync-245-fifo 模式的所有功能,收发字节长度不再必须是 FTDI 芯片位宽的倍数。 + +  + +# 简介 + +**sync-245-fifo 模式** 是 FTDI 公司的 USB 系列芯片的最高速的传输模式。本库将 sync-245-fifo 控制器封装成 Verilog 模块 (IP顶层模块名为 **ftdi_245fifo_top** ),它具有标准 AXI-stream 收发接口 ,供 Verilog 开发者调用。 + +除了 IP 核本身,本库还提供配套的: + +* 仿真代码 (Verilog) +* IP 核调用示例 (Verilog),实现了一些简单的通信业务逻辑:回环、发送指定长度数据包等。供开发者直接上板测试。 + +对于电脑 (上位机) ,我提供了: +* 驱动安装教程 +* Python3 FTDI USB 软件库 (ftd2xx 和 ftd3xx) 的安装教程 +* 几个 Python3 程序用于进行 USB 通信测试 + + +> 注:虽然上位机的示例代码是用 Python 编写的 (我习惯用 Python) ,但你也可以用其它语言 (C++, C# 等) 编写上位机程序与 FTDI 芯片通信。FPGA 上的设计**并不局限你在上位机上使用哪种编程语言**。 + +  + +**图1**是该系统工作时的结构框图。 + +| ![module_structure](./figures/structure.png) | +| :------------------------------------------: | +| **图1**:系统框图 | + +  + +## 特点 + +* **纯 Verilog 编写**,方便仿真,支持 Altera, Xilinx 等各厂商的 FPGA 。 +* **标准 AXI-stream 接口**:用户通过两路 AXI-stream 收发数据 (一路发,一路收)。 +* **收发调度**:FTDI USB 芯片与 FPGA 之间的接口是半双工的,该模块调度收发分时复用,在用户看来,收发的 AXI-stream 是独立的。 +* **跨时钟域**:FTDI USB 芯片有自己的时钟 (ftdi_clk),该时钟并不一直运行,不能作为 FPGA 的主时钟。该模块实现了时钟域转换。 +* **位宽变换**:本模块实现了位宽变换。使得 AXI-stream 接口的 **位宽可自定义** 。 + +  + +## 性能测试结果 + +| 芯片型号 | FT232H / FT2232H | FT600 | FT601 | +| :--------: | :------------: | :------: | :--------: | +| USB模式 | USB2.0 HS | USB3.0 SS | USB3.0 SS | +| 理论速率 | 60MB/s | 200MB/s | 400MB/s | +| 实测速率 (FPGA→上位机) | 42MB/s | 140MB/s | 未测 | +| 实测速率 (上位机→FPGA) | 35MB/s | 175MB/s | 未测 | + +  + +  + +# ftdi_245fifo_top 模块说明 + +Verilog 设计代码见 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录中的 [ftdi_245fifo_top.v](./RTL/ftdi_245fifo/ftdi_245fifo_top.v) ,它是IP的顶层,可供 FPGA 开发者调用来开发自己的 USB 通信业务。它的接口和参数如**图2**。 + +| ![module_ports](./figures/ports.png) | +| :----------------------------------: | +| **图2**:ftdi_245fifo_top.v 的接口 | + +  + +## 模块参数 + +要调用本模块,首先要根据实际情况指定各个参数 (parameter) 的值,如下表: + +| parameter | 说明 | +| ----------- | ------------------------------------------------------------ | +| `TX_EW` | 决定了用户发送接口的数据宽度 (即 `tx_tdata` 的宽度) : 0对应8bit宽,1对应16bit宽,2对应32bit宽,3对应64bit宽,…… 可以根据需要任意设置,不受 USB 芯片型号限制。 | +| `TX_EA` | 决定了用户发送缓存的深度,深度=2^`TX_EA`。默认为10 (深度为1024) ,如果 FPGA BRAM 较大,该项可以设得更大,来提高突发性能。 | +| `RX_EW` | 决定了用户接收接口的数据宽度 (即 `rx_tdata` 的宽度) : 0对应8bit宽,1对应16bit宽,2对应32bit宽,3对应64bit宽,…… 可以根据需要任意设置,不受 USB 芯片型号限制。 | +| `RX_EA` | 决定了用户接收缓存的深度,深度=2^RX_AEXP。默认为10(即默认深度为1024),如果 FPGA BRAM 较大,该项可以设得更大,来提高突发性能。 | +| `CHIP_TYPE` | 必须是 `"FTx232H"` , `"FT600"` , 或 `"FT601"` | + +  + +## 模块接口:复位 + +复位信号见 ftdi_245fifo_top.v 代码中的: + +```verilog +input wire rstn_async; +``` + +`rstn_async` 是模块的全局异步复位信号,`1'b0` 代表复位,`1'b1` 代表释放复位。在使用中,该信号可以直接设为 `1'b1` ,或者连接到工程的复位信号上。 + +  + +## 模块接口:连接 FTDI 芯片 + +FTDI 芯片信号见 ftdi_245fifo_top.v 代码中的: + +```verilog +// 连接 FTDI 芯片的信号 ------------------------------ +input wire ftdi_clk; +input wire ftdi_rxf_n; +input wire ftdi_txe_n; +output wire ftdi_oe_n; +output wire ftdi_rd_n; +output wire ftdi_wr_n; +inout [(8< :warning: FTDI 芯片只能收发单纯的数据流,而没有数据包的概念,因此 `tx_tlast` 仅仅用于控制是否立即发送。数据发到电脑上后,上位机的软件无法知道哪里是 `tx_last` 指定的包的分界点。 + +  + +## 模块接口:AXI-stream master 接收接口 + +接收接口是标准 AXI-stream master ,用户通过它接收来自 FTDI 芯片 (来自电脑) 的数据。对应代码中的: + +```verilog +// AXI-stream master 接收接口 ------------------------------ +input wire rx_clk; // AXI-stream 时钟 +input wire rx_tready; +output wire rx_tvalid; +output wire [(8< :warning: FTDI 芯片只能收发单纯的数据流,而没有数据包的概念,因此 `rx_tlast` 仅仅用于指示当前数据是不是 FTDI 芯片缓存里拿出的最后一个数据。上位机的软件上无法控制 `rx_tlast` 信号。例如,如果你在软件中调用 usb.send() 函数两次,发送了两次数据,发到 FPGA 上后这两次数据可能连在一起,中间不一定有 `rx_tlast=1` 分界。 + +  + +## 关于 AXI-stream tvalid&tready 的进一步说明 + + `tready` 和 `tvalid` 的握手举例如下波形图,用户接口发送了3个数据:D1, D2, D3 。其中: + +* 第 1, 2 周期,用户令 `tx_tvalid=0` ,因此暂时空闲没发数据。 + +* 第 3 周期,用户要发 D1,因此令 `tx_tvalid=1`,本周期 `tx_tready=1` ,说明 D1 即刻发送成功。 + +* 第 4, 5, 6, 7 周期,用户要发 D2,因此令 `tx_tvalid=1`,但第 4, 5, 6 周期 `tx_tready=0` 导致发送暂时失败,直到第 7 周期 `tx_tready=1` 时,才发送成功。 + +* 第 8, 9 周期,用户令 `tx_tvalid=0` ,因此暂时空闲没发数据。 + +* 第 10 周期,用户要发 D3,因此令 `tx_tvalid=1`,本周期 `tx_tready=1` ,D3 即刻发送成功。 + +``` +cycle 1 2 3 4 5 6 7 8 9 10 11 + _ __ __ __ __ __ __ __ __ __ __ __ + clk \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \ + _____________________________ _____ +tx_tvalid ___________/ \___________/ \________ + _________________ ________________________________ +tx_tready \_________________/ + _____ _______________________ _____ +tx_tdata/tx_tkeep/tx_tlast XXXXXXXXXXXX__D1_X___________D2__________XXXXXXXXXXXXX__D3_XXXXXXXXX +``` + +  + +  + +模块仿真 +============================= + +仿真相关的文件在 SIM 目录中,其中: + +- tb_ftdi_chip_model.v 模拟了一个简单的 FTDI 芯片的行为。 +- tb_ftdi_245fifo.v 是 testbench 的顶层,它把 tb_ftdi_chip_model.v 和待测模块 (也即 [ftdi_245fifo_top.v](./RTL/ftdi_245fifo/ftdi_245fifo_top.v) ) 连起来进行仿真。 +- tb_ftdi_245fifo_run_iverilog.bat 是运行 iverilog 仿真的命令脚本 (仅对Windows)。 + +该仿真的行为是:FTDI 芯片不断发送不断递增的字节给 FPGA ,FPGA 将 AXI-stream 发送接口和接收接口回环 (loopback) 连接,因此数据会被发回 FTDI 芯片,因此 FTDI 芯片会收到不断递增的字节。 + +## 使用 iverilog 仿真 + +使用 iverilog 进行仿真前,需要安装 iverilog ,见:[iverilog_usage](https://github.com/WangXuan95/WangXuan95/blob/main/iverilog_usage/iverilog_usage.md) + +然后双击 tb_ftdi_245fifo_run_iverilog.bat 运行仿真,然后可以打开生成的 dump.vcd 文件查看波形。 + +## 使用其它仿真工具 + +需要把 SIM 目录和 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录中 的所有 .v 文件加入仿真工程,以 tb_ftdi_245fifo.v 为仿真的顶层模块,然后运行仿真。 + +  + +  + +# 开始使用 FT232H + +FT232H 是 USB2.0 High Speed 通信芯片,本例实现 FPGA 通过 FT232H 与上位机进行通信。 + +> :warning: 本例也适用于 FT2232H (具体操作时需要你自己变通变通),因为 FT2232H 与 FT232H 高度相似。注意 FT2232H 的 **channel-A** 可配置成 **sync-245-fifo 模式** ,而 **channel-B** 则不行。 +> + +## 步骤1:安装驱动和库 + +请按照 [FTD2XX_guide.md](./FTD2XX_guide.md) 的指示在上位机 (以Windows系统为例) 上安装 FTD2XX 驱动和 Python FTD2XX 库。 + +## 步骤2:部署 FPGA 工程 + +以下是该 FPGA 工程的模块层次结构: + +- **fpga_top_ft232h_loopback.v** (在 [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) 目录),是整个FPGA工程的顶层 + - **clock_beat.v** (在 [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) 目录) + - **ftdi_245fifo_top.v** (在 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录),是 ftdi_245fifo_top IP核的顶层 + - [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 文件夹中的**其它.v文件**,它们会被 ftdi_245fifo_top.v 调用 + +请将以上 .v 文件加入工程。 + +然后,请在 FPGA 工程中进行引脚约束,具体而言,你需要查看电路板的原理图 (或开发板配套资料) ,了解 FT232H 的各引脚被连接到了 FPGA 的哪些引脚号上;然后,把 FPGA 工程的顶层的信号分配到对应的 FPGA 引脚号上,使得这些信号与 FT232H 的引脚对应起来。对应关系见下表 (该表简化自 [FT232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf) 第9页) : + +| FT232H引脚号 | FT232H引脚名 | FT232H引脚名 (245-fifo 模式下) | 应约束到顶层的信号名 | +| :----------: | :----------: | :----------------------------: | :------------------------: | +| 13 | ADBUS0 | D0 | ftdi_data[0] | +| 14 | ADBUS1 | D1 | ftdi_data[1] | +| 15 | ADBUS2 | D2 | ftdi_data[2] | +| 16 | ADBUS3 | D3 | ftdi_data[3] | +| 17 | ADBUS4 | D4 | ftdi_data[4] | +| 18 | ADBUS5 | D5 | ftdi_data[5] | +| 19 | ADBUS6 | D6 | ftdi_data[6] | +| 20 | ADBUS7 | D7 | ftdi_data[7] | +| 21 | ACBUS0 | RXF\# | ftdi_rxf_n | +| 25 | ACBUS1 | TXE\# | ftdi_txe_n | +| 26 | ACBUS2 | RD\# | ftdi_rd_n | +| 27 | ACBUS3 | WR\# | ftdi_wr_n | +| 28 | ACBUS4 | SIWU\# | ftdi_siwu (=1) :warning: | +| 29 | ACBUS5 | CLKOUT | ftdi_clk | +| 30 | ACBUS6 | OE\# | ftdi_oe_n | +| 31 | ACBUS7 | PWRSAV\# | ftdi_pwrsav (=1) :warning: | +| 34 | RESET\# | RESET\# | ftdi_resetn (=1) :warning: | + +> :warning: SIWU\# 、 PWRSAV\# 和 RESET\# 实际上是要永远被拉为高电平的 (你也可以看到在 fpga_top_example_ft232h.v 中它们被 assign 为 1) ,因此有些板子的设计直接用电阻把它们拉到高电平上,而没有连接到 FPGA ,对于这种情况,就不用在 FPGA 约束它们的引脚,并直接在 fpga_top_example_ft232h.v 中注释掉与它们相关的语句即可。 +> + +  + +如果你要自己画 FT232H 的 PCB,可参考**图3** 。 + +| ![ft232h_example_sch](./figures/ft232h_example_sch.png) | +| :-----------------------------------------------------: | +| **图3**:FT232H USB 接口电路的参考原理图设计 | + +  + +关于本工程的时钟和时序约束: + +- FPGA 工程存在两个时钟:主时钟 (clk) 和 FT232H 芯片提供的时钟 (ftdi_clk) 。 +- 对于 FT232H , ftdi_clk 频率为 60MHz 。 +- clk 的频率比较自由,几 MHz 到几百 MHz 都可以。最简单的就是直接用开发板的晶振。 +- `constraint_ft232h.sdc` 展示了时序约束示例,它约束了: + - ftdi_clk 的频率 (60MHz) + - clk 的频率 (你需要根据实际频率去约束) + - 指定 ftdi_clk 和 clk 为异步关系 +- `constraint_ft232h.sdc` 直接适用于 Altera Quartus 工程,对于 Xilinx 的 xdc ,约束语句应该差不多 (可能有点小区别,请自行修改) +- 如果懒得进行时序约束,也可以不约束,大概率不会有问题。 + +  + +在 FPGA 工程中,另外注意以下几点: + +* LED[3:0] 信号连接了4颗 LED 灯 (不是必须的) ,其中: + * 如果 ftdi_clk 启动,LED[3] 会闪烁,否则不闪烁。一般 ftdi_clk 只会在第一次通信时启动,而不是上电就启动。 + * LED[2:0] 展示上次收到的数据的最低 3 bit。 +* ftdi_245fifo_top 模块的参数 `CHIP_TYPE` 被设为 `"FTx232H"` ,这是因为我们用的是 FT232H / FT2232H 芯片。 + +然后你就可以编译工程,步骤略。 + +  + +## 步骤3:烧录 FT232H 芯片 + +针对每颗 FT232H 芯片,需要在初次使用时烧录为 sync-245-fifo 模式。每颗 FT232H 芯片只需要烧录一次,因为 FT232H 外围会有个 EEPROM 芯片用来永久保存配置,之后每次使用都不需要再烧录 (除非你又烧录了其它模式) 。 + +首先进入 [FTDI官网下载页面](https://ftdichip.com/utilities/#ft_prog) 下载并安装 **FT\_Prog** 软件。然后进行以下步骤: + +* 在电脑上插入你需要烧录的 FT232H 的 USB 接口,并拔出所有其它 FTDI USB 设备 (包括拔出所有的 FPGA 下载器,因为很多 FPGA 下载器,例如 Xilinx Digilent 下载器是 FT2232H 芯片实现的,如果万一覆盖了下载器内部的程序,你的下载器就废了) 。 +* 打开 **FT\_Prog** 软件。 +* 如**图4**,点击 **Scan and Parse** (小放大镜) ,扫描出插在该电脑的所有 FTDI 芯片,找到 FT232H 对应的芯片。 +* 在 FT232H 下方的属性树中逐级展开,找到并点击 Hardware 。 +* 在右侧选择 245 FIFO 模式。 +* 点击上方工具栏中的 **Program** (小闪电) 。 +* 弹出确认窗口,点击 Program。烧录到 FT232H 。 +* 烧录后,需要重新拔插 FT232H 的 USB 接口,该配置才能生效。 + +| ![ft232h_program](./figures/ft232h_program.png) | +| :-----------------------------------------------: | +| **图4**:烧录 FT232H 芯片,配置为 245-fifo 模式。 | + +  + +## 步骤4:在电脑上运行通信程序 + +运行这些程序之前,请确保 FT232H 已经按照步骤3被烧录为 sync-245-fifo 模式,且 FPGA 中下载了步骤2中建立的工程。 + +我在 [python](./python) 文件夹中提供了以下几个 Python 程序,它们会通过 FT232H 与 FPGA 进行通信。 + +| 文件名 | 功能 | +| ----------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | 定义了 USB_FTX232H_FT60X_sync245mode 类,用来对 FTDI USB 设备进行打开、关闭、发送数据、接收数据。适用于 FT232H、FT2232H、FT600、FT601 。会被以下程序调用。 | +| usb_loopback_simple.py | 上位机先发送 16 字节给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这 16 字节。该程序和 FPGA 顶层设计 fpga_top_ft232h_loopback.v 配套。 | +| usb_loopback_mass.py | 上位机先发送发送大量数据给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这些字节,程序会判断收到的数据和之前发出去的数据是否一致,来验证回环的正确性。该程序和 FPGA 顶层设计 fpga_top_ft232h_loopback.v 配套。 | + +  + +  + +# 开始使用 FT600 + +FT600 是 USB3.0 Super Speed 通信芯片,本例实现 FPGA 通过 FT600 与上位机进行通信。 + +## 步骤1:安装驱动和库 + +请按照 [FTD3XX_guide.md](./FTD3XX_guide.md) 的指示在 Host-PC (Windows系统) 上安装 FTD3XX 驱动和 Python FTD3XX 库。 + +## 步骤2:部署 FPGA 工程 + +以下是该 FPGA 工程的模块层次结构: + +- **fpga_top_ft600_loopback.v** (在 [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) 目录),是整个FPGA工程的顶层 + - **clock_beat.v** (在 [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) 目录) + - **ftdi_245fifo_top.v** (在 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录),是 ftdi_245fifo_top IP核的顶层 + - [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 文件夹中的**其它.v文件**,它们会被 ftdi_245fifo_top.v 调用 + +请将以上 .v 文件加入工程。 + +然后,请在 FPGA 工程中进行引脚约束,具体而言,你需要查看电路板的原理图 (或开发板配套资料),了解 FT600 的各引脚被连接到了 FPGA 的哪些引脚号上;然后,把 FPGA 工程的顶层的信号分配到对应的 FPGA 引脚号上,使得这些信号与 FT600 的引脚对应起来。对应关系见下表 (该表简化自 [FT600 DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT600Q-FT601Q%20IC%20Datasheet.pdf) 第 7\~10 页) : + +| FT600引脚号 | FT600引脚名 | 应约束到顶层的信号名 | +| :---------: | :---------: | :-----------------------------------------: | +| 33 | DATA_0 | ftdi_data[0] | +| 34 | DATA_1 | ftdi_data[1] | +| 35 | DATA_2 | ftdi_data[2] | +| 36 | DATA_3 | ftdi_data[3] | +| 39 | DATA_4 | ftdi_data[4] | +| 40 | DATA_5 | ftdi_data[5] | +| 41 | DATA_6 | ftdi_data[6] | +| 42 | DATA_7 | ftdi_data[7] | +| 45 | DATA_8 | ftdi_data[8] | +| 46 | DATA_9 | ftdi_data[9] | +| 47 | DATA_10 | ftdi_data[10] | +| 48 | DATA_11 | ftdi_data[11] | +| 53 | DATA_12 | ftdi_data[12] | +| 54 | DATA_13 | ftdi_data[13] | +| 55 | DATA_14 | ftdi_data[14] | +| 56 | DATA_15 | ftdi_data[15] | +| 2 | BE_0 | ftdi_be[0] | +| 3 | BE_1 | ftdi_be[1] | +| 43 | CLKOUT | ftdi_clk | +| 5 | RXF_N | ftdi_rxf | +| 4 | TXE_N | ftdi_txe | +| 9 | OE_N | ftdi_oe | +| 8 | RD_N | ftdi_rd | +| 7 | WR_N | ftdi_wr | +| 6 | SIWU_N | ftdi_siwu (=1) :warning: | +| 10 | RESET_N | ftdi_resetn (=1) :warning: | +| 11 | WAKEUP_N | ftdi_wakeupn (=0) :triangular_flag_on_post: | +| 12 | GPIO0 | ftdi_gpio0 (=0) :triangular_flag_on_post: | +| 13 | GPIO1 | ftdi_gpio1 (=0) :triangular_flag_on_post: | + +> :warning: SIWU_N 和 RESET_N 实际上是要永远被拉为高电平的,因此有些板子的设计直接用电阻把它们拉到高电平上,而没有连接到 FPGA ,对于这种情况,就不用在 FPGA 约束它们的引脚,并直接在 fpga_top_example_ft600.v 中注释掉与它们相关的语句即可。 +> +> :triangular_flag_on_post: WAKEUP_N 、 GPIO0 和 GPIO1 实际上是要永远被拉为低电平的,因此有些板子的设计直接用电阻把它们拉到GND上,而没有连接到 FPGA ,对于这种情况,就不用在 FPGA 约束它们的引脚,并直接在 fpga_top_example_ft600.v 中注释掉与它们相关的语句即可。 + +  + +如果你要自己画 FT600 的 PCB,可参考**图5** 。 + +| ![ft600_example_sch](./figures/ft600_example_sch.png) | +| :---------------------------------------------------: | +| **图5**:FT600 USB 接口电路的参考原理图设计 | + +  + +关于本工程的时钟和时序约束: + +- FPGA 工程存在两个时钟:主时钟 (clk) 和 FT600 芯片提供的时钟 (ftdi_clk) 。 +- 对于 FT600 / FT601 , ftdi_clk 频率为 100MHz 。 +- clk 的频率比较自由,几 MHz 到几百 MHz 都可以。最简单的就是直接用开发板的晶振。 +- `constraint_ft600.sdc` 展示了时序约束示例,它约束了: + - ftdi_clk 的频率 (100MHz) + - clk 的频率 (你需要根据实际频率去约束) + - 指定 ftdi_clk 和 clk 为异步关系 +- `constraint_ft600.sdc` 直接适用于 Altera Quartus 工程,对于 Xilinx 的 xdc ,约束语句应该差不多 (可能有点小区别,请自行修改) +- 如果懒得进行时序约束,也可以不约束,大概率不会有问题。 + +  + +在 FPGA 工程中,另外注意以下几点: + +* LED[3:0] 信号连接了4颗 LED 灯 (不是必须的) ,其中: + * 如果 ftdi_clk 启动,LED[3] 会闪烁,否则不闪烁。一般 ftdi_clk 只会在第一次通信时启动,而不是上电就启动。 + * LED[2:0] 展示上次收到的数据的最低 3 bit。 +* ftdi_245fifo_top 模块的参数 `CHIP_TYPE` 被设为 `"FT600"` ,这是因为我们用的是 FT600 芯片。 + +然后你就可以编译工程,步骤略。 + +  + +## 步骤3:在电脑上运行通信程序 + +运行这些程序之前,请确保 FPGA 中下载了步骤2中建立的工程。 + +我在 [python](./python) 文件夹中提供了以下几个 Python 程序,它们会通过 FT600 与 FPGA 进行通信。 + +| 文件名 | 功能 | +| ---------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | 定义了 USB_FTX232H_FT60X_sync245mode 类,用来对 FTDI USB 设备进行打开、关闭、发送数据、接收数据。适用于 FT232H、FT2232H、FT600、FT601 。会被以下程序调用。 | +| usb_loopback_simple.py | 上位机先发送 16 字节给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这 16 字节。该程序和 FPGA 顶层设计 fpga_top_ft600_loopback.v 配套。 | +| usb_loopback_mass.py | 上位机先发送发送大量数据给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这些字节,程序会判断收到的数据和之前发出去的数据是否一致,来验证回环的正确性。该程序和 FPGA 顶层设计 fpga_top_ft600_loopback.v 配套。 | + +  + +# 参考资料 + +* FT232H 芯片资料:http://www.ftdichip.cn/Products/ICs/FT232H.htm +* FT232H 软件示例:http://www.ftdichip.cn/Support/SoftwareExamples/CodeExamples.htm +* FT600 芯片资料:http://www.ftdichip.cn/Products/ICs/FT600.html +* FT600/FT601 软件示例: http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm diff --git a/RTL/fpga_ft232h_example/clock_beat.v b/RTL/fpga_ft232h_example/clock_beat.v new file mode 100644 index 0000000..75d4979 --- /dev/null +++ b/RTL/fpga_ft232h_example/clock_beat.v @@ -0,0 +1,31 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : clock_beat +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: When clk runs, beat signal will blink +//-------------------------------------------------------------------------------------------------------- + +module clock_beat # ( + parameter CLK_FREQ = 50000000, // Unit:Hz + parameter BEAT_FREQ = 5 // Unit:Hz +) ( + input wire clk, + output reg beat +); + +localparam CYCLES = (CLK_FREQ / 2 / BEAT_FREQ); + +reg [31:0] count = 0; + +initial beat = 1'b0; + +always @ (posedge clk) + if ( count < (CYCLES-1) ) begin + count <= count + 1; + end else begin + count <= 0; + beat <= ~beat; + end + +endmodule diff --git a/RTL/fpga_ft232h_example/constraint_ft232h.sdc b/RTL/fpga_ft232h_example/constraint_ft232h.sdc new file mode 100644 index 0000000..e6bdde1 --- /dev/null +++ b/RTL/fpga_ft232h_example/constraint_ft232h.sdc @@ -0,0 +1,17 @@ +## Constraints required for design. +## Please modify it according to your actual situation. +## +## Note: +## The clock of FT600/FT601 chip (ftdi_clk) is 100MHz +## The clock of FT232H/FT232H chip (ftdi_clk) is 60MHz +## It is also necessary to declare that the FPGA's main clock and ftdi_clk clock are asynchronous. Because they only transmit data through asynchronous FIFO +## + +## 50MHz FPGA main clock (usually generated by oscillator) +create_clock -name clk -period 20.000 [get_ports clk] + +## 60MHz FT232H chip clock (generated by FT232H) +create_clock -name ftdi_clk -period 16.667 [get_ports ftdi_clk] + +## Declare that these two clocks are asynchronous +set_clock_groups -asynchronous -group {clk} -group {ftdi_clk} diff --git a/RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v b/RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v new file mode 100644 index 0000000..823335c --- /dev/null +++ b/RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v @@ -0,0 +1,115 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fpga_top_ft232h_loopback +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: an example of ftdi_245fifo_top +// the pins of this module should connect to FT232H chip +// This design only sends all data received from the FTDI chip back to the FTDI chip. +// It is also known as 'loopback' +//-------------------------------------------------------------------------------------------------------- + +module fpga_top_ft232h_loopback ( + input wire clk, // main clock, connect to on-board crystal oscillator + + output wire [ 3:0] LED, + + // USB2.0 HS (FT232H chip) ------------------------------------------------------------ + //output wire ftdi_resetn, // to FT232H's pin34 (RESET#) , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + //output wire ftdi_pwrsav, // to FT232H's pin31 (PWRSAV#), !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + //output wire ftdi_siwu, // to FT232H's pin28 (SIWU#) , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + input wire ftdi_clk, // to FT232H's pin29 (CLKOUT) + input wire ftdi_rxf_n, // to FT232H's pin21 (RXF#) + input wire ftdi_txe_n, // to FT232H's pin25 (TXE#) + output wire ftdi_oe_n, // to FT232H's pin30 (OE#) + output wire ftdi_rd_n, // to FT232H's pin26 (RD#) + output wire ftdi_wr_n, // to FT232H's pin27 (WR#) + inout [ 7:0] ftdi_data // to FT232H's pin20~13 (ADBUS7~ADBUS0) +); + + + +//assign ftdi_resetn = 1'b1; // 1=normal operation , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! +//assign ftdi_pwrsav = 1'b1; // 1=normal operation , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! +//assign ftdi_siwu = 1'b1; // 1=send immidiently , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + + + + +//----------------------------------------------------------------------------------------------------------------------------- +// user AXI-stream signals (loopback) +//----------------------------------------------------------------------------------------------------------------------------- +localparam AXIS_EW = 2; +wire tready; +wire tvalid; +wire [(8<> 4); + calculate_crc = TABLE_CRC[calculate_crc[3:0]] ^ (calculate_crc >> 4); +end +endfunction + + +reg state = 1'b0; // 0:inputting stream, 1:outputting CRC +reg [31:0] crc = 'hFFFFFFFF; +reg [31:0] len = 0; + +reg [31:0] crc_tmp; // not real register +reg [31:0] len_tmp; // not real register + +integer i; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + state <= 1'b0; + crc <= 'hFFFFFFFF; + len <= 0; + end else begin + if (state == 1'b0) begin // inputting stream + if (i_tvalid) begin + crc_tmp = crc; + len_tmp = len; + for (i=0; i<(1<= 4) begin + length <= length - 4; + end else begin + length <= 0; + state <= RX_BYTE0; + end + end + endcase + end + + +assign i_tready = (state != TX_DATA); + +assign o_tvalid = (state == TX_DATA); + +assign o_tdata = {length[7:0] - 8'd4, + length[7:0] - 8'd3, + length[7:0] - 8'd2, + length[7:0] - 8'd1 }; + +assign o_tkeep = (length>=4) ? 4'b1111 : + (length==3) ? 4'b0111 : + (length==2) ? 4'b0011 : + (length==1) ? 4'b0001 : + /*length==0*/ 4'b0000; + +assign o_tlast = (length>=4) ? 1'b0 : 1'b1; + + +endmodule diff --git a/RTL/fpga_ft600_example/clock_beat.v b/RTL/fpga_ft600_example/clock_beat.v new file mode 100644 index 0000000..75d4979 --- /dev/null +++ b/RTL/fpga_ft600_example/clock_beat.v @@ -0,0 +1,31 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : clock_beat +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: When clk runs, beat signal will blink +//-------------------------------------------------------------------------------------------------------- + +module clock_beat # ( + parameter CLK_FREQ = 50000000, // Unit:Hz + parameter BEAT_FREQ = 5 // Unit:Hz +) ( + input wire clk, + output reg beat +); + +localparam CYCLES = (CLK_FREQ / 2 / BEAT_FREQ); + +reg [31:0] count = 0; + +initial beat = 1'b0; + +always @ (posedge clk) + if ( count < (CYCLES-1) ) begin + count <= count + 1; + end else begin + count <= 0; + beat <= ~beat; + end + +endmodule diff --git a/RTL/fpga_ft600_example/constraint_ft600.sdc b/RTL/fpga_ft600_example/constraint_ft600.sdc new file mode 100644 index 0000000..8582d0b --- /dev/null +++ b/RTL/fpga_ft600_example/constraint_ft600.sdc @@ -0,0 +1,17 @@ +## Constraints required for design. +## Please modify it according to your actual situation. +## +## Note: +## The clock of FT600/FT601 chip (ftdi_clk) is 100MHz +## The clock of FT232H/FT232H chip (ftdi_clk) is 60MHz +## It is also necessary to declare that the FPGA's main clock and ftdi_clk clock are asynchronous. Because they only transmit data through asynchronous FIFO +## + +## 50MHz FPGA main clock (usually generated by oscillator) +create_clock -name clk -period 20.000 [get_ports clk] + +## 100MHz FT600 chip clock (generated by FT600) +create_clock -name ftdi_clk -period 10.000 [get_ports ftdi_clk] + +## Declare that these two clocks are asynchronous +set_clock_groups -asynchronous -group {clk} -group {ftdi_clk} diff --git a/RTL/fpga_ft600_example/fpga_top_ft600_loopback.v b/RTL/fpga_ft600_example/fpga_top_ft600_loopback.v new file mode 100644 index 0000000..15a8678 --- /dev/null +++ b/RTL/fpga_ft600_example/fpga_top_ft600_loopback.v @@ -0,0 +1,120 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fpga_top_ft600_loopback +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: an example of ftdi_245fifo_top +// the pins of this module should connect to FT600 chip +// This design only sends all data received from the FTDI chip back to the FTDI chip. +// It is also known as 'loopback' +//-------------------------------------------------------------------------------------------------------- + +module fpga_top_ft600_loopback ( + input wire clk, // main clock, connect to on-board crystal oscillator + + output wire [ 3:0] LED, + + // USB3.0 (FT600 chip) ------------------------------------------------------------ + //output wire ftdi_resetn, // to FT600's pin10 (RESET_N) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_wakeupn, // to FT600's pin11 (WAKEUP_N), !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_gpio0, // to FT600's pin12 (GPIO0) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_gpio1, // to FT600's pin13 (GPIO1) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_siwu, // to FT600's pin6 (SIWU_N) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + input wire ftdi_clk, // to FT600's pin43 (CLK) + input wire ftdi_rxf_n, // to FT600's pin5 (RXF_N) + input wire ftdi_txe_n, // to FT600's pin4 (TXE_N) + output wire ftdi_oe_n, // to FT600's pin9 (OE_N) + output wire ftdi_rd_n, // to FT600's pin8 (RD_N) + output wire ftdi_wr_n, // to FT600's pin7 (WR_N) + inout [15:0] ftdi_data, // to FT600's pin56~53 (DATA_15~DATA_12) , pin48~45 (DATA_11~DATA_8) , pin42~39 (DATA_7~DATA4) and pin36~33 (DATA_3~DATA_0) + inout [ 1:0] ftdi_be // to FT600's pin3 (BE_1) and pin2 (BE_0) +); + + + +//assign ftdi_resetn = 1'b1; // 1=normal operation , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_wakeupn= 1'b0; // 0=wake up , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_gpio0 = 1'b0; // GPIO[1:0]=00 = 245fifo mode , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_gpio1 = 1'b0; // , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_siwu = 1'b1; // 1=send immidiently , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + + + + +//----------------------------------------------------------------------------------------------------------------------------- +// user AXI-stream signals (loopback) +//----------------------------------------------------------------------------------------------------------------------------- +localparam AXIS_EW = 1; + +wire tready; +wire tvalid; +wire [(8<> 4); + calculate_crc = TABLE_CRC[calculate_crc[3:0]] ^ (calculate_crc >> 4); +end +endfunction + + +reg state = 1'b0; // 0:inputting stream, 1:outputting CRC +reg [31:0] crc = 'hFFFFFFFF; +reg [31:0] len = 0; + +reg [31:0] crc_tmp; // not real register +reg [31:0] len_tmp; // not real register + +integer i; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + state <= 1'b0; + crc <= 'hFFFFFFFF; + len <= 0; + end else begin + if (state == 1'b0) begin // inputting stream + if (i_tvalid) begin + crc_tmp = crc; + len_tmp = len; + for (i=0; i<(1<= 4) begin + length <= length - 4; + end else begin + length <= 0; + state <= RX_BYTE0; + end + end + endcase + end + + +assign i_tready = (state != TX_DATA); + +assign o_tvalid = (state == TX_DATA); + +assign o_tdata = {length[7:0] - 8'd4, + length[7:0] - 8'd3, + length[7:0] - 8'd2, + length[7:0] - 8'd1 }; + +assign o_tkeep = (length>=4) ? 4'b1111 : + (length==3) ? 4'b0111 : + (length==2) ? 4'b0011 : + (length==1) ? 4'b0001 : + /*length==0*/ 4'b0000; + +assign o_tlast = (length>=4) ? 1'b0 : 1'b1; + + +endmodule diff --git a/RTL/ftdi_245fifo/axi_stream_assert.v b/RTL/ftdi_245fifo/axi_stream_assert.v new file mode 100644 index 0000000..cc7ea9a --- /dev/null +++ b/RTL/ftdi_245fifo/axi_stream_assert.v @@ -0,0 +1,107 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : axi_stream_assert +// Type : simulation only (will be ingnored by synthesizer) +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: assert whether a AXI-stream interface follows the AXI-stream standards +// see AMBA 4 AXI4-stream Protocol Version: 1.0 Specification +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_assert #( + parameter NAME = "", + parameter BW = 4, // data byte-width, e.g., 4 means 4-byte-width (32-bit) + parameter ASSERT_MASTER_NOT_Z_OR_X = 1, + parameter ASSERT_SLAVE_NOT_Z_OR_X = 1, + parameter ASSERT_MASTER_NOT_CHANGE_WHEN_HANDSHAKE_FAILED = 1, + parameter ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED = 0, + parameter ASSERT_PACKED = 0 +) ( + input wire clk, + input wire tready, + input wire tvalid, + input wire [8*BW-1:0] tdata, + input wire [ BW-1:0] tkeep, + input wire tlast +); + + +localparam [8*BW-1:0] TDATA_ALL_ZERO = 0; +localparam [ BW-1:0] TKEEP_ALL_ZERO = 0; +localparam [ BW-1:0] TKEEP_ALL_ONE = ~TKEEP_ALL_ZERO; + + +reg tready_d = 1'b0; +reg tvalid_d = 1'b0; +reg [8*BW-1:0] tdata_d = TDATA_ALL_ZERO; +reg [ BW-1:0] tkeep_d = TKEEP_ALL_ZERO; +reg tlast_d = 1'b0; + + +always @ (posedge clk) begin + tready_d <= tready; + tvalid_d <= tvalid; + tdata_d <= tdata; + tkeep_d <= tkeep; + tlast_d <= tlast; +end + + +generate if (ASSERT_SLAVE_NOT_Z_OR_X) begin + always @ (posedge clk) begin + if (tready ===1'bz || tready ===1'bx) begin $display("*** error : %s AXI-stream tready = X or Z", NAME); $stop; end + end +end endgenerate + + +generate if (ASSERT_MASTER_NOT_Z_OR_X) begin + always @ (posedge clk) begin + if (tvalid ===1'bz || tvalid ===1'bx) begin $display("*** error : %s AXI-stream tvalid = X or Z", NAME); $stop; end + if (tvalid) begin + if ((&tdata)===1'bz || (&tdata)===1'bx) begin $display("*** error : %s AXI-stream tdata = X or Z", NAME); $stop; end + if ((&tkeep)===1'bz || (&tkeep)===1'bx) begin $display("*** error : %s AXI-stream tdata = X or Z", NAME); $stop; end + if ((&tlast)===1'bz || (&tlast)===1'bx) begin $display("*** error : %s AXI-stream tdata = X or Z", NAME); $stop; end + end + end +end endgenerate + + +generate if (ASSERT_MASTER_NOT_CHANGE_WHEN_HANDSHAKE_FAILED) begin + always @ (posedge clk) + if ((~tready_d) & tvalid_d) begin // At last cycle, sender sended a data, but receiver not avaiable, assert that sender is still sending a data at this cycle, and assert that data not change + if ( 1'b1 !== tvalid) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal withdraw tvalid", NAME); $stop; end + if (tdata_d !== tdata ) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal change of tdata", NAME); $stop; end + if (tlast_d !== tlast ) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal change of tlast", NAME); $stop; end + if (tkeep_d !== tkeep ) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal change of tkeep", NAME); $stop; end + end +end endgenerate + + +generate if (ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED) begin + always @ (posedge clk) + if (tready_d & (~tvalid_d)) begin // At last cycle, receiver avaiable, but sender not sended a data, assert that receiver is still avaiable at this cycle + if (1'b1 !== tready) begin $display("*** error : %s AXI-stream receiver behavior abnormal : Illegal withdraw tready", NAME); $stop; end + end +end endgenerate + + +generate if (ASSERT_PACKED) begin + integer i; + reg has_zero; + always @ (posedge clk) + if (tvalid) begin + if (~tlast) begin + if (tkeep !== TKEEP_ALL_ONE) begin $display("*** error : %s AXI-stream not packing", NAME); $stop; end + end else begin + has_zero = 1'b0; + for (i=0; i 2.3.3 -> downsizing considerations +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_downsizing #( + parameter IEW = 0, // input width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + parameter OEW = 0 // output width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + // OEW must < IEW +) ( + input wire rstn, + input wire clk, + // AXI-stream slave + output wire i_tready, + input wire i_tvalid, + input wire [(8< 2.3.3 -> packing +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_packing #( + parameter EW = 2, // AXI byte width is 1<> 1; // equal to 1< (i_count, i_bytes) + i_bytes = 0; + i_count = 0; + for (i=0; i<(1< POW_EW) begin $display("***error : axi_stream_packing_imm"); $stop; end + + + + +reg unflushed = 1'b0; +reg r_tlast = 1'b0; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + unflushed <= 1'b0; + r_count <= 0; + r_bytes <= 0; + o_tvalid <= 1'b0; + r_tlast <= 1'b0; + o_tdata <= 0; + o_tkeep <= TKEEP_ALL_ZERO; + end else begin + if (o_tready) + o_tvalid <= 1'b0; + if (o_tready | ~o_tvalid) begin // o_tready=1 or o_tvalid=0, then it is available to put a new data to output + if (unflushed) begin + unflushed <= 1'b0; + r_count <= 0; + r_bytes <= 0; + o_tvalid <= 1'b1; + r_tlast <= 1'b1; + o_tdata <= r_bytes; + o_tkeep <= ~(TKEEP_ALL_ONE << r_count); // r_count of bytes is available, so there will be r_count of '1' on tkeep + end else begin + if (i_tvalid) begin + if (t_count > POW_EW_W) begin // More than (1< 2.3.3 -> downsizing considerations / upsizing considerations +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_resizing #( + parameter IEW = 0, // input width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + parameter OEW = 0 // output width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... +) ( + input wire rstn, + input wire clk, + // AXI-stream slave + output wire i_tready, + input wire i_tvalid, + input wire [(8< OEW) begin + + axi_stream_downsizing #( + .IEW ( IEW ), + .OEW ( OEW ) + ) u_axi_stream_downsizing ( + .rstn ( rstn ), + .clk ( clk ), + .i_tready ( i_tready ), + .i_tvalid ( i_tvalid ), + .i_tdata ( i_tdata ), + .i_tkeep ( i_tkeep ), + .i_tlast ( i_tlast ), + .o_tready ( o_tready ), + .o_tvalid ( o_tvalid ), + .o_tdata ( o_tdata ), + .o_tkeep ( o_tkeep ), + .o_tlast ( o_tlast ) + ); + +end else begin // (IEW==OEW) + + assign i_tready = o_tready; + assign o_tvalid = i_tvalid; + assign o_tdata = i_tdata; + assign o_tkeep = i_tkeep; + assign o_tlast = i_tlast; + +end endgenerate + + +endmodule diff --git a/RTL/ftdi_245fifo/axi_stream_upsizing.v b/RTL/ftdi_245fifo/axi_stream_upsizing.v new file mode 100644 index 0000000..3343b82 --- /dev/null +++ b/RTL/ftdi_245fifo/axi_stream_upsizing.v @@ -0,0 +1,127 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : axi_stream_upsizing +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: AXI-stream upsizing or downsizing, +// see AMBA 4 AXI4-stream Protocol Version: 1.0 Specification -> 2.3.3 -> upsizing considerations +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_upsizing #( + parameter IEW = 0, // input width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + parameter OEW = 0 // output width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + // OEW must > IEW +) ( + input wire rstn, + input wire clk, + // AXI-stream slave + output wire i_tready, + input wire i_tvalid, + input wire [(8<IEW) ? (OEW-IEW) : 1; + +localparam [DIFF_EW-1:0] IDX_ONE = 1; +localparam [DIFF_EW-1:0] IDX_FIRST = {DIFF_EW{1'b0}}; +localparam [DIFF_EW-1:0] IDX_LAST = ~IDX_FIRST; + +reg [DIFF_EW-1:0] idx = IDX_FIRST; + +reg tmp_full = 1'b0; +reg [(8< data1 + end + end else if (data1_en) begin // half full (data1 exist, but data2 not exist) + if (o_rdy & i_en) begin // allow both output and input simultaneously + data1 <= i_data; // i_data -> data1 + end else if (o_rdy) begin // only allow output + data1_en <= 1'b0; // set data1 to not exist + end else if (i_en) begin // only allow input + data2_en_n <= 1'b0; // set data1 to exist + data2 <= i_data; // i_data -> data2 + end + end else begin // empty (both data1 & data2 not exist) + if (i_en) begin // allow input + data1_en <= 1'b1; // set data1 to exist + data1 <= i_data; // i_data -> data1 + end + end + end + +endmodule diff --git a/RTL/ftdi_245fifo/fifo4.v b/RTL/ftdi_245fifo/fifo4.v new file mode 100644 index 0000000..29314f0 --- /dev/null +++ b/RTL/ftdi_245fifo/fifo4.v @@ -0,0 +1,98 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fifo4 +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: fifo with capacity=4, +// all outputs are register-out, which can be inserted into the stream chain to improve timing +// it has a 'almost_full' pin to report half-full (already 2 data in the fifo) +//-------------------------------------------------------------------------------------------------------- + +module fifo4 # ( + parameter DW = 8 +) ( + input wire rstn, + input wire clk, + output wire i_almost_full, + output wire i_rdy, + input wire i_en, + input wire [DW-1:0] i_data, + input wire o_rdy, + output wire o_en, + output wire [DW-1:0] o_data +); + + +reg data1_en = 1'b0; // fifo data1 exist +reg data2_en = 1'b0; // fifo data2 exist +reg data3_en = 1'b0; // fifo data3 exist +reg data4_en = 1'b0; // fifo data4 exist +reg [DW-1:0] data1 = 0; // fifo data1 +reg [DW-1:0] data2 = 0; // fifo data2 +reg [DW-1:0] data3 = 0; // fifo data3 +reg [DW-1:0] data4 = 0; // fifo data4 + +assign i_rdy = data4_en; +assign i_almost_full = data2_en; + +assign o_en = data1_en; +assign o_data = data1; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + data1_en <= 1'b0; + data2_en <= 1'b0; + data3_en <= 1'b0; + data4_en <= 1'b0; + data1 <= 0; + data2 <= 0; + data3 <= 0; + data4 <= 0; + end else begin + if (data4_en) begin + if (o_rdy) begin + data4_en <= 1'b0; + {data1, data2, data3} <= {data2, data3, data4}; + end + + end else if (data3_en) begin + if (o_rdy & i_en) begin + {data1, data2, data3} <= {data2, data3, i_data}; + end else if (o_rdy) begin + data3_en <= 1'b0; + {data1, data2} <= {data2, data3}; + end else if (i_en) begin + data4_en <= 1'b1; + data4 <= i_data; + end + + end else if (data2_en) begin + if (o_rdy & i_en) begin + {data1, data2} <= {data2, i_data}; + end else if (o_rdy) begin + data2_en <= 1'b0; + data1 <= data2; + end else if (i_en) begin + data3_en <= 1'b1; + data3 <= i_data; + end + + end else if (data1_en) begin + if (o_rdy & i_en) begin + data1 <= i_data; + end else if (o_rdy) begin + data1_en <= 1'b0; + end else if (i_en) begin + data2_en <= 1'b1; + data2 <= i_data; + end + + end else begin + if (i_en) begin + data1_en <= 1'b1; + data1 <= i_data; + end + end + end + +endmodule diff --git a/RTL/ftdi_245fifo/fifo_async.v b/RTL/ftdi_245fifo/fifo_async.v new file mode 100644 index 0000000..9afec46 --- /dev/null +++ b/RTL/ftdi_245fifo/fifo_async.v @@ -0,0 +1,97 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fifo_async +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: asynchronous fifo +//-------------------------------------------------------------------------------------------------------- + +module fifo_async #( + parameter DW = 8, + parameter EA = 10 +)( + input wire i_rstn, + input wire i_clk, + output wire i_tready, + input wire i_tvalid, + input wire [DW-1:0] i_tdata, + input wire o_rstn, + input wire o_clk, + input wire o_tready, + output reg o_tvalid, + output reg [DW-1:0] o_tdata +); + + + +reg [DW-1:0] buffer [(1<> 1) ^ wptr; +wire [EA:0] rptr_grey = (rptr >> 1) ^ rptr; +wire [EA:0] rptr_next_grey = (rptr_next >> 1) ^ rptr_next; + +always @ (posedge i_clk or negedge i_rstn) + if(~i_rstn) + wq_wptr_grey <= 0; + else + wq_wptr_grey <= wptr_grey; + +always @ (posedge o_clk or negedge o_rstn) + if(~o_rstn) + {rq2_wptr_grey, rq1_wptr_grey} <= 0; + else + {rq2_wptr_grey, rq1_wptr_grey} <= {rq1_wptr_grey, wq_wptr_grey}; + +always @ (posedge o_clk or negedge o_rstn) + if(~o_rstn) + rq_rptr_grey <= 0; + else + rq_rptr_grey <= rptr_grey; + +always @ (posedge i_clk or negedge i_rstn) + if(~i_rstn) + {wq2_rptr_grey, wq1_rptr_grey} <= 0; + else + {wq2_rptr_grey, wq1_rptr_grey} <= {wq1_rptr_grey, rq_rptr_grey}; + +wire w_full = (wq2_rptr_grey == {~wptr_grey[EA:EA-1], wptr_grey[EA-2:0]} ); +wire r_empty = (rq2_wptr_grey == rptr_next_grey ); + +assign i_tready = ~w_full; + + + +always @ (posedge i_clk or negedge i_rstn) + if(~i_rstn) begin + wptr <= 0; + end else begin + if(i_tvalid & ~w_full) + wptr <= wptr + {{EA{1'b0}}, 1'b1}; + end + +always @ (posedge i_clk) + if(i_tvalid & ~w_full) + buffer[wptr[EA-1:0]] <= i_tdata; + + + +initial o_tvalid = 1'b0; + +always @ (posedge o_clk or negedge o_rstn) + if (~o_rstn) begin + rptr <= 0; + o_tvalid <= 1'b0; + end else begin + rptr <= rptr_next; + o_tvalid <= ~r_empty; + end + +always @ (posedge o_clk) + o_tdata <= buffer[rptr_next[EA-1:0]]; + + +endmodule diff --git a/RTL/ftdi_245fifo/fifo_delay_submit.v b/RTL/ftdi_245fifo/fifo_delay_submit.v new file mode 100644 index 0000000..abc3ccd --- /dev/null +++ b/RTL/ftdi_245fifo/fifo_delay_submit.v @@ -0,0 +1,85 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fifo_delay_submit +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: +//-------------------------------------------------------------------------------------------------------- + +module fifo_delay_submit #( + parameter DW = 8 // AXI byte width is 1< SUBMIT_CHUNK ) begin + wptr_submit <= wptr; + count <= 11'd0; + end else begin + count <= (i_en & i_rdy) ? 11'd0 : (count+11'd1); + if (count == 11'd2047) + wptr_submit <= wptr; + end + end + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + wptr <= 11'h0; + end else begin + if (i_en & i_rdy) + wptr <= wptr + 11'h1; + end + +always @ (posedge clk) + if (i_en & i_rdy) + buffer[wptr[9:0]] <= i_data; + + + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + rptr <= 11'h0; + o_en <= 1'b0; + end else begin + rptr <= rptr_next; + o_en <= (rptr_next != wptr_submit_d); + end + +always @ (posedge clk) + o_data <= buffer[rptr_next[9:0]]; + + +endmodule diff --git a/RTL/ftdi_245fifo/ftdi_245fifo_fsm.v b/RTL/ftdi_245fifo/ftdi_245fifo_fsm.v new file mode 100644 index 0000000..fea176f --- /dev/null +++ b/RTL/ftdi_245fifo/ftdi_245fifo_fsm.v @@ -0,0 +1,129 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : ftdi_245fifo_fsm +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: FTDI USB chip's 245fifo mode's main control FSM, called by ftdi_245fifo_fsm +//-------------------------------------------------------------------------------------------------------- + +module ftdi_245fifo_fsm #( + parameter CHIP_EW = 0, // FTDI USB chip data width, 0=8bit, 1=16bit, 2=32bit. for FT232H is 0, for FT600 is 1, for FT601 is 2. + parameter CHIP_DRIVE_AT_NEGEDGE = 0 +) ( + input wire rstn, + input wire clk, + output wire tx_tready, + input wire tx_tvalid, + input wire [(8< USB -> PC), AXI-stream slave. + tx_clk, // User-specified clock for send interface + tx_tready, + tx_tvalid, + tx_tdata, + tx_tkeep, + tx_tlast, + // user recv interface (PC -> USB -> FPGA), AXI-stream master. + rx_clk, // User-specified clock for recv interface + rx_tready, + rx_tvalid, + rx_tdata, + rx_tkeep, + rx_tlast, + // FTDI 245FIFO interface, connect these signals to FTDI USB chip + ftdi_clk, + ftdi_rxf_n, + ftdi_txe_n, + ftdi_oe_n, + ftdi_rd_n, + ftdi_wr_n, + ftdi_data, + ftdi_be // only FT600&FT601 have BE signal, ignore it when the chip don't have this signal +); + + + +localparam CHIP_EW = (CHIP_TYPE=="FT601") ? 2 : (CHIP_TYPE=="FT600") ? 1 : 0; +localparam CHIP_DRIVE_AT_NEGEDGE = (CHIP_TYPE=="FT601") ? 0 : (CHIP_TYPE=="FT600") ? 0 : 0; + + +//----------------------------------------------------------------------------------------------------------------------------- +// in/out signals +//----------------------------------------------------------------------------------------------------------------------------- +input wire rstn_async; +input wire tx_clk; +output wire tx_tready; +input wire tx_tvalid; +input wire [(8< CHIP_EW) ? TX_EW : CHIP_EW; +localparam RX_FIFO_EW = (RX_EW > CHIP_EW) ? RX_EW : CHIP_EW; + +wire tx_a_tready; +wire tx_a_tvalid; +wire [(8<= TX_FIFO_EW ) +) u_tx_packing ( + .rstn ( rstn_tx ), + .clk ( tx_clk ), + .i_tready ( tx_a_tready ), + .i_tvalid ( tx_a_tvalid ), + .i_tdata ( tx_a_tdata ), + .i_tkeep ( tx_a_tkeep ), + .i_tlast ( tx_a_tlast ), + .o_tready ( tx_b_tready ), + .o_tvalid ( tx_b_tvalid ), + .o_tdata ( tx_b_tdata ), + .o_tkeep ( tx_b_tkeep ), + .o_tlast ( tx_b_tlast ) +); + + +axi_stream_resizing #( + .IEW ( TX_EW ), + .OEW ( TX_FIFO_EW ) +) u_tx_upsizing ( + .rstn ( rstn_tx ), + .clk ( tx_clk ), + .i_tready ( tx_b_tready ), + .i_tvalid ( tx_b_tvalid ), + .i_tdata ( tx_b_tdata ), + .i_tkeep ( tx_b_tkeep ), + .i_tlast ( tx_b_tlast ), + .o_tready ( tx_c_tready ), + .o_tvalid ( tx_c_tvalid ), + .o_tdata ( tx_c_tdata ), + .o_tkeep ( tx_c_tkeep ), + .o_tlast ( ) +); + + +fifo_async #( + .DW ( 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 bz$2P7OU`$Xn&Xv@n<~kxKPh-@^6vito~;AQ literal 0 HcmV?d00001 diff --git a/figures/ft232h_example_sch.png b/figures/ft232h_example_sch.png new file mode 100644 index 0000000000000000000000000000000000000000..b73a7fc1ade0aeac7480e19161abf4787cce2edc GIT binary patch literal 33310 zcmeFZcRZE<8$X`CM}#;D3E35n?9r4CWraibJ~x@!Gb)iik5y!kW8KKgCVO^{nCm}tB=l;Jx|BnLyf1<# z*NmMyGr71O_G;V@<`l?Zx=K2FPxmLv6@I=_O)zg%E3m^-MlU|LskF{6M3va~1PGFY zY9p4Q4BxAIFh4KOMiw8EO{2zK69qVy&MiE)>;C-_QS`fG?znP6nN|L_m5*(>;6qH8uy=7(Bl2C~`qh7gH%Bx1<_`U-M~N?}v#eKpMjW`qK7dzqDy z^N)u(4Q0*$-O~|NvRahZPRScRTY37S$VNm^h94QIw&J|1Ei3a`(Rne0bZ+a1g#7bf zJ9VfkTIDm6I@Gxbd=LEZA4r1`U_{&gmwQq~b)aBPk~S@<*82Xs)mmvbO$fm35kD1q z`5dUmjHe`Ga_aKs+j=kyrc~%lq)<)*j@Y($Csvy! zNGT|n2!M5dipB|WhXx)SE)g{Yef!F z3+sF(N)pGL2oy}oEydjXlWz{)1PQL!Y2JRAU<7ZnO>(XvJrfRHur{11=RTOj|8`OxErc;}X8F3z(Gj+#eRxPNXNlI|gByy%!<_yw4?oAj>;jME(co z(iOq=PbX6>Hst|MidxQAG?$^xj3?Cc89)__mQDguc}_Gwn?lmYz-v1)X2SER%xx)N zc!w1d09^9WX)#>&ixdhaTHl!av$ zLk|S0e^a&&^^r|o#)BU*0vYl==jWp2QZ~oJ7i>J)KKni^ZMUV0V%$5`WRFdNu&VHkD;=LqssbtXSvl)$w(8@ z7qj!T!nhaPZX(G5y%z%S6##CR?>SM`f3!<6JvW?p&6d!eT_q7^0*ye`)3LK~V?cwJ`WGMbbIXg%%#XR2=>z-4gP1%9t~% zo4b|F39?aYw1J}adDc&UZE>P_wZzQ^nrMWzYhPAga$z+`;0?$x0yV<0YS@%j|2R-e z1~V;Z*hHvTHe)6UN-pN($?O&k*{!In;e0aLOjIE4T8NDPi1X{eEB7=#; zZVTL{#GjP?B}{#sxR%XdQ6PjP*8DhCRH}~0^skxaF~Tgi?ef@Dy$ZH`1|xdb_q@>t z->~L&nTDtTZhZJI!nnod*eJel{D~qMmdhr>vQ2>h4XQh}Lyl6}i55dAoCL5_2mtMs zV8Z6xRC$s3i4_@b>TG_!$q=ftu z9o}}F?n2I0h|AQ_#u|}Q@nrMvwcRZHc5g3)(s7?UHl-XTH;9}dA%Qt?)01gS?cO!T z^Wdn1GlLCecU{JBB6OeqKTVUeaBM0l4LI&W)hkaqE z&uf1ZqhQI0?T(6Gd+#6jOJmZC=kXiE&|y$^7B3CyU9j>W8@-Itd}cm`mBrVivoBzd z89-N>h=}Y&fbpRtH)883uSe3lqxOadXM#EU;@!-Bjr~`iF(L@fi-UScoJ^6^3BU7% z`bqed9p+#Er@xs?x#QVu(Gxuj06f#`RcqD&CBxNNN^ZW#lm-b_kjmZi*TA2!9%9sN z{v+L`5FWcanZ_QLIy>}}!V71XYY1$9ONpNF&{jz!&eIF4i5GWzqp@E|o*X}_6wf2@ zDbB;gWi24@)U5|c!hsj}AWZIK5o_oLN7Z|b)BX)5!GVlafMu?7HlOvu7{=*`rCz8| zmcoe8z5(%gohvZ)Iz4C_dPbqUnB#Bi$RQ!NLHBR3A9k{&dqu5vV{|vs7IlDmKO6|* z!9%03SYtl9*rsD%0rc5xbt_^0`{5JC{`$j9zQVEMy^qt&AEXG`4a5_~3CjFa~pt(sVduhsWbmQABD zPR3i^o!-`UGIz75@>pNQ)WIr1jY%+WXc;hKuWG=yv>}y{a^1D4#jwVg+1>Hnm@tV! zFlnPd!-fxm6W43=r4iXw5;Uw!w|@u3yAF<4_>JIdi!^E>d-2ba_n;i605^!2Fqjm-<@RIm=6dC0ews0tVR9!SKQmS5W0m;z6E=6@&g z2enoqXK#sObcA4F#RlPG8IocS5eQ!ElBCekXOPd#3f5lON)U@Nh%EX*3D}ycVfX)% zZPX3s!s=-_Q~Dd3T)JhVR^I)Wn`4e!_V*(m!O?=>TYZ}<`c-1GN6gjaFo{57o72yE)LWvjK$No{0!FfC^-3vym-9w;Yvc*!Mgp9+>zFNbpxIbxUeFeO%vKS*FT zdL+90a5Uh#SVuxO@lcKO;X2FLWcL$ce^5iu^#)Z7<i4Q)l|4eG+9$oX&$wZPd?CNrexp%~N4%J9DNo{t9VI* z?qfug+#tBO(-m!;PY!P!sZVzlB+i1~#pITG>{l+13aaQb%_E(=b9DMLE8!uvo<-8hL&R#)FnBpW;dmkmi(pT&Vxe=Lf%+x<$cGYxooEgw@ zpd%o?8ts!Bd_ZEe|Mo}1RCvkNdZBu&XD=!hPOm}{4$kBltL-VB#7dcSn@H7vK?$%V?WNbDCw&b{{=@9ZyIAhiN#E?W}>^+ zU3s!SQl%mjIsPa!IN#L?+R$786q)LRfu`}g_e1%l-k6e4>|)a=T_M81-S{FG3AG8YfPX(PdFSm<0GP;rAjWLpF6 z&grpc!(~GKjo>Ke`V@uG3JfElj-(c|s=r{vJ5>sk-9Mh<57-Kg`u z)&0j-p8u*${0uypAc@o48W;;HzPkK*?*WqAT$QnxhS>aLe_RHN;8y-)hhJ{U+oM^F z+Sw2Y(%QZw?p%H{WT_R@5vT?^v_JF%DP{X7>wu3vd^s5v;;ioAV3_*0M;L7LZ1 zD$7{0w~!37Y*8B1?p63l;c}dy$m!R_I2qLO9Qw#e?RY8k3(L5Xg5axy*TA5^;7AS; ze3bn7N3%J)J^6f}P@b-D!vpnrp+5GWX1&-KXy3YA-AV>e)h)OC0y^hJ)|2A_y6)jf z6<@@n$CPj^pH-0m_SL(O+%Sj+uZBg?TGTJQ*|z%}SEOPjr(NA_;%%pmVCm=gvu4F<4ZN#@Nj;X!VwspxM6;0(@h-+k1}8@Eo1^4PF@j}VfW`f{%XFnk*c)1jr5$ z6u109TR0v!TYce%(Ff59QaXrHldoUfS-~R1V?&-72U~M-!aX-t7MW3)sEY?>QKF#F zXy`f1&5rA9j7I=^>c3#nEu~E}zW*!xw)+b#FD+*!G4_h!p^HsehV5MfJ4(=I%ngCJ z5O2BUym59T4l6{8xjp;-p*Vi|DXWnYdJ*naaRsw?^ns5z56QK&zYSF-MNleOdUnv zC*6JYFQDjkUqvNwQCDSXBF%fpA_?G5b@x;UldP6rVvY}EI629_K#s}zHwM{nScI6@ z0ZR#4B~lISZyt|wNWQvE=fW~YEY}y)>O&slD+H^mz+R)s)L;Ouk>vBfL@=?|YP<$i zL#ziUN;9N)d>>pcJSewxbpwwk?-)$6%W0lx#Q_TSX^K*;nPQlpVxZ0O*m9iLs9-c* z{MexKmK>B{=VgURKNSM(M*h%39&^G#&w^)H?d;R2@%~YPWnUoz=)*<_yB8=OFk`mB zR#8O8*hI0-j}iS`vliVR4_Dc191@ES&vFQEJeapbRX(GJaeA2Sahey|g)9EnUs1`R z^xe@5ZNbTm2NV?Y!iJHNAM?#M4r7W9AL;vt%zA|e(t+ZZh;B$r;2%AHdEM~W`$QmH zzw*J%==X$B;HwqlN1l)!wx47kkQoLUiVd$`8Y}BChTT61y&mG+fpS>D26^_o7$_%_ z;w?oZapSLnR;O~g|E*Io;@+c^o~D=XvEp5zxSy>B+gtA0QTIv!bv^-gEY5_ouH=9Q zSOh$Z;srHv%hjZnU#-wPd^MR_{tOVZ!m{l!i`AbCh=^ax8@x~7Y;o1=Tda?>89luadF7(_vLFbqJHv0jFH@hPHyy-7)vN@$?w#xVV6 zr~r0y<%QL1hM}u42;6K%V8Yt_Kfk!_f_~gZicz3}3b)xSQA*?CxmG_9rL($h2>DOr zeX0j1iXT*=g4U*Gh%X%EJ%?UOmuMD=EJfeYg1BQSjK`;5*gzU*oLj=|CbK=jp{D!- z?udsSX>);`u_yxIX7=|ladf57&K;v$@Lbv%8zk7}em4-|;R@q?f*0cO=$XzzK==^y( zMWarn?3b@f?-cG&bIK^M9lJyTS538}(0owVTZ)UJKx;e5y^(7V%5Xi@#4oeVsDlnr z6qoZ>G_7G~MvfTw$gSiNrHQ8%+T7oQf!blP*jZ>3DKq}?i8u)TzIhf?4HKiATM z%*zf~8^pGT>4DMxsd#oaPBkZ1FD%$=eIxw5HEIA^d&jr&o4{-rlXZo(19y*}O+`!( z^u03_P+3mc(&4#VSr*r(RT%{^jWHU>0LZwMtKD*-@c1M4&yG5kcS{12O8zY1zdyt>4xkO zFK)YK4@}IFGVO2dJ4&G&FVo?{0@~p%SjdIU(H1x2t1HW;!p6)YJ@389uH>c zNFeW1oBc5%b4Jxf4hl`%dsSKHJgBnGj`#8d1ID>$n3*?(HhBg>?)BWO8^InD`9&G} zNLXDQ1*H_I#r_r@z5}>YZa_bp;=;JD>zb8Y<^G!*>ItQ|AyEv&e8(wI+@Cc9PffTQ zOZaFS2mk0?YLRa(eAR7pLGxnT*US%$ueC^uraRd;k*hOa2+fi-bL{E##8eh?KN(O# zw*ppjjnO}DRzc}rB?)_LLa`aM_H>xAp*}Fp@@i`rlD;??7zw~5O`{e!VdxA_W{xw= z8oqXazmE5^1AW|=3eNn;NH`(i#PDB=1zIXS#2hNadws7wvw~mo$k5HiRzE8AHw?!2|ChSt+YP`N^1X~%-Qn;5&4mHEi_pyqKB~oT%xy+$a!|hrBfBc- z1(EcS!gJnsopzhsO$Q|o5C2Y{7Rv1Nf`P#Bo~nh=iz6;LB1+<$wcBVnzF(4Kx7!1R>6n5VTV1c2q%0Z!0Sq2Hqu0um#=luBj2tuFngH<1boF5#9ot}UEL0|(fbb6Zd=*SY6!PLc8k1dHaVD(icN<4^}09XUQw^i7idhWk}pg3PfQtx&YA-b~l z!Cnz2t!XL{T{UvKNJz&?HVn>1DZzjk;Py(!;E1rzWS(}hvY?4+gYMfuwcP@2W)`V>S7pm84Fkf_m zB?ebEx^Wq89c}GHR_q>weuEsZ5T~ov{*(@kK2Hx+hn!9td~0>^@F5O%G2_4EZ*iuQ z(5KWa_&#rCkef)V5{F}Xi;P6Y_r!*MiM85|VAV5cM*&^Xe(hIC$lD$h0VxF8ZLsgS z^Ln5vFSK2BS10Qj=h84$Bbhe5eYE)p$S5cX-Gr>IeU)yCx4k!gd1!>?z=!w_e1Di~ ziNy(;*bqSn&v|dGkGe62Cl!Ft4J$zY!ee#6&x3Xd)J@ixz%aG?Wyo;Pp?i*jZKSwQ zk*}ubw2%9Dgdy49>hhzxQt7$;UFw#^k0Iy`Hiw? z;M^HLyDeqy;%DQPh+kx2cy)H@%&eAlW%L2g5IqFicCPuRTsYG+9O`Ca+|<*8hz1>u zU+a`D!Io-8IyytPD9ds6g6_WV#PO_D8|#UjM+}q539NujRb@He^ry!H*)`St7LjU- zeMl9Z^&Z8^p&4tRSS9`EydcEI@@O)IjKx%6p@!!~49D|OONB=pOM1sG_ap-D@ z!Ypp7j~wxhlLKvS_%*j>aAq>kAlvgmwy52^ZZ>r9pT|j zkYN)As8(PNO{7jsL+{w}2dL~l)QvjF>{fV|estN|NJE44r|$Q{eZ@%Kz9l;xwA|fw zCOt)?GMSYdE+If2+fVC;*N01irwYlAu!syMs+zWGDz~xNG{NJDw3re{PB!A*v+ZP| zt8fLbUSzh|U}#dtU3#3$l>_|i6I1|x1{H9`EhUVq*wKBxp|%`oQU2gV7! zp@-yIWn;@7ayc&g3`Ny7<6qbK`WN)SRg4AvPaQ9tLmYcEcT7{}jIWr)lpNASKeVh}u|yA9bouMR zVkQh1d80=46+bncfScY}+Keg#K3uI>;@pP~@~J;&%gsSszjm!01u(a{nQ;uCHrRyr zbq5zB41SJ*mkte1Vd(ItmT`#*%lr7Y`zM6b=M+-5S;gU=n71RzJi51|`Kd8s1|of+ zULf`g;n?OV_zBn$AOoddvpeIq_3f33pBd96-cbRt3FgYnr4a2hAbu+SmYdft9>N&j7+a4@!YqMW%vqLz+K<(ZD>BKF#bj%a0 z^^Nn$(s8cEzxBf<*@+hZ;`_KrdWeHNYM$DL=A%W$NbtsyKMi^d{HkDV3Nzghe6|p@%@&wBUEpBhY;2w7$ zQaSlag3Wjn*QMyve@7EvUmLA>)FwzxlIpjf1|rT&6HsI%`5M#;jTr7E{9WFbl)?Bph?TG6T&Y4?cq|elD<{ z9P25G)4`Rv)yyT%v2ZcPrAciV!r{Q2-#NWkh8^IE-b1y4o31_!9=iWAhu8~tm+xOb z%5%O^90#aRd!H1|7|MM!Pz)-{u^!F*gX1yF{EyN0&9~*R;+G-Qmmbv^-PZy0$>k{sL?t}v&6m=i0x9Z#?YOKs2BR7rxJT2OSviT7tQY8#(rcL_aQ^prR;|) zLZ6Y>hLKpZE2A=N3A9{rt;;rML#PKbJiY0*xDB$s-3(kNOzTO-vN4*J+|(AxMr3=K z{dR;sPr0Nx-s=|BG-LV|c?LJBGMXb~X)0#uq_1che@5z`C7}D%M&kWYz(e z=^~X6H16kXVCRlJ?C*4WVPBF{oO7`Zh%XptZNW|VT&QO9o1?VS|E%0^xpTSB4)s(n zK~px%x(P7(n)_crgng))EmWL&+mDzI-Advr z?HhAQ0QOoJ@2S&#Vmc)hOlmHYJWb-2^phe^GN(Pue!F&mfvfZ#;3zDakG#;#g4qz2 z{f1OLS`KiJ0A%1c6pt~HL+vnf&GB7nfmxU94nn4j_G(`=ywnqaMN-* z{C$bD-HMGUX0Q@j1$|&ly^F>0Refhmc|7j^DrQF`_a;58`#NA*;t=9te?zVZ?~wAZ zr{)b=n#HDC?MSDN-wS{`7841~zRdEekpuOcob+#m-z&94H!4BKun*>jL_jTn^`L0J zRN4>U2AmUe){U&|e3(3VI9g_tRqO`u7=Tvj$wCJ&wb?NmR(BspYSCd5jex6o%FJ-U z@1(FB?=W_=5%NX@wyYEjz}VA$!U1x6YyqhBzU=%GSh!%hmYG})IBac6Lw{b*v#UG} z14haSQ@>O!vu6dhihFIMq&b9=0@pRQnac*X8g4mY7;Ou9{68aO6y`HZ(uW#Etg%d* zf*)2EnY0_q&7q1nrfI-H-t!yA*Lw~uG%HkCw440{v08+S(b6idgIP2R0<-e|x<^25 z=C>mlL`JVA6suzTOpeq+{X7peDoXBDDZ=YfFwk1sE;Wp>q0M{)EGYTzHQ?W#zfZr* zV!LTHjI$_Ha+RwOVAi}=;i~rqS&?;~BJ1g4p~_v@p%EkO{$Z8Z3_s&OMo{CC!$`kR zhMLZ_V%*}~1uPWoz7De-D^hC{>A zbzT^#RbB1P4T0`bF(o*z}CXUzC>`IPg!{@RbuU zB8vwhM=sEM-2Ux)uwJGrt7IZ~TjyUA45a#r=`c#`LGkKIzY_$&Zx{W?>RH*plB{LH z0{ljyU*ZVxE5Rlb$EsST=8m|sgM7$bILKx`$n(Ag5R)WE@eOp*1x#9hqwyg4sR!w) z8Wk|+OHEBgF1>#?;*2(+>a2#qLb9{W@P!qk`ZU8d0#Mo|D{S+|ZvN)5hN|RwKPo%8 z{uhvNLhOS{DpZi8lKxFrAy2h%CkG4eSap!?D-5;r;(!;frbD@XrSo6wI>)wmA-zF* zueNRQqo=CL18JF+$di+Hd0zO`(Upim8?dkT5B(VKz^zCL-H<1nE}~-$W7lC4qcho0 z4>eHxCjXv>09}~eFwe^~v}dU`oE9aSnXhZR;Q$KAKc=Y^E(;`h^R4DFu08hw2|EoQ z+hn-1RAA?crkL~uNyt{t->Ib27VSUqI-&%mg3;wQBWAqU>ovh3TBc*1ZI*vqxrz&q zi5K5Ro9B{6WIv|jQ>~!M_25F^#ENG*EtDRz;9sMNzg-73M!|9>D__JzJW+6^Z0BKx z`^WKsH`>0T^1R=Ssne%y`R^DKiG3eZBCPz=Er7^0XmHy zYjWTvpN@+^wF=9as&UX3Z+fR9P8^ISV+_Cb!{wKej zWx;U2#~7Y)T%j4S1PkhFFGChvd3skAiACYvR^!Z1ub_iy?L z{*}pcWru*BwSM`J{TsnG-C47RjJ-E736hNhT1NE#jp&UsyC81x$mw-!kRO7`6D4r4 zy=GI9tzC9m2Zr7LiK8WhBCJH5n^3kNceNN=ysacN*zJ_2g=)hbx4jXkjeG4o@G(=3 zYN1IIVKW2ee0}l3^Ocdne!m$~*)MR>1Um4=ynXlS-Qgi>Ouh?A&iBKRl;`c;n8B3R zpZ#jbwHPOBuRn^*{+6UClQf1k>825n;^c5L4qv5@_?< zL(7{r7KjANhYMS6SATRD{L`4*v&Mu~t#lpuPcuE-2!39f!{mqAc(a?d-erTq9?0$^ z@M)Q2G+?bcWH6Xn1>_}eFMnFw1}lqVFo+kvtgcO&tEsQ(_HU0I=B~0xCA|m04)C@hBp(nqUr>g(znd0<|%OiA9SJQ3n4$^n|YmSG!W2Q z&-j}5Vp(LuSaja;WKV;sn%MhHZfDKrZ4pd@P)??G;IC@9&jvhFF5)+h1VUgi$}|Cx zT;2~SZUmE`Zvkm<)xHF}-d4C`+s847YVqqQlW3n!RH~50&*0dLIfniBY8gnR| z06KBUHpwQb4_|;xB%I9u9l4kP>CD~04aR`DMW1bY1zcGyAVFn4B*fqbmt86xpK!+p z7BfE3CV=<3V-=x4(h$o6FrPR2O?w5dMPWKv3+hn=LX%Rx{;c8|ilU#vc<1=RS)fE1 zJc2`h=tX09ygcuR^kTBHPc1l2?$}^plOKGO8pj8%zR370oN0+YVWWJ0Wu9;-=|atd z=`X=8xe;zi+<3BffI;g~b%uyNp?Y~|`JjSqhZB04da-zya=~m|!-K;dd%Pr88AMEC zAc2%^A^n~t=R(#A5+j)v$1-h^4?_szJt1qsb$gR|z}m|J7sLeqErt0 zppB<$D((P^@KlYQH#nxD17pgu?o0ETT^LbBk9^1)ktcPQg>$Y^``@$}gp)ofJCU9? z+!_SKJymUYAbUqcSMFGfT49Ec)L)xrN$yJl^=X$3Y@cvUI}~_6RO%QXjExx>vd%~x zDy-1;e9;CT?;p03>|3td@ZulSiA!HFM?IX4~@!u_CQ^_8S zY{kGy*hv%l#D&(#F{xTR)?X8$@5~{2u&j`*s&F^zX+f}2l+OW$6#V>cZf7D81r*eevwCOcA8Ki^HEI0vy;MvGlHHsbF94tAitwBa^43q)o{Ej)_)li7V83VN(7%ZH4RUQhkoH)+gN=2>WOHrq2g zk2%v3x*u%f22+1NZC*HYIFF|Hd}%O@a3V(TbCdH)@l6+*dwA;xO)b%=UX#%7cXNxr z=d(l5j!HsZpW;YMxy|ejEg;yjF9bi$0y;5g#clH0G(cp&8ob7jaXQf(!APcuVL22& zV02|}++*K6?ECu6fGUq&%o1BhHdbv^12eC9T+E(Q?G*$>nDRrIcZpWm(l>Gd-!Hp{ z?eCSuS!gcC@!+2y2Pj@b_CZV4y6)~d(Q-1D)Njmt9hmAQiN@!g=bK+wplAlkHve*- zDx&mjU*ip;bDzBU?`XBS0!vu*_FcW8;8bxBL9j12v=;&d;GUT#J(@<;JH^?TK>vF1 zoD_aTM&Y2HI`X4>bDl<0w4X#Q5NiwlQo? zc^Y~Te)+rYiLaW2Yn7b&U-^Dsg6@08BlA13ePwyx=)yWbqbgpk$@VHvbD13{Edz z4a5Q$-NLla$!5xYzRRz0JFys@+Sngi3d;AogpD1PHXyG3)Q`O)vgE1yt_J#cPcdR9 zr+@9}(F6(SmrG-2MROnxbn;rn{W;Xc*vp8q3PyOSfdcxWpt0Ur)AppK+OUZRckBY; zz*g)B&4`g7K}yMf*W@Wxrz~8EaRVttnKD`#Uvt%}Dw2FkH<%(RY7(9^hm5oLNIg~e zDtWWcaSN2UQ=3fNrit)6CWk&}zNu|RedXhwrMWj6Z><&XnBhRM?W#%5yR~uhc z_qX^PQY~t|1tg2fbU44d!;*B;E2Xg&_*zKye)ZtgEys4wr{6+b#=W5XaDxV$rXd`@ zHrv30H0XM{Xiok5A|*GRAXr=3HkGkD`HzXO2hVf~n3_;^RsrjwaFn`T!hPvKQrw<^ z$ySwGPn%rzg()$)hXq+g=Ek4mp~lVpRL&m`i57?3Ft-&+ zKS?KRXMZn_rdkxVh2Usbf+amI<+`lCaxuz?kp6+HnqixK^EtW zOU7FdYb>6%pZFZ%7<-NI5#FR8YyyOCjt>kNQ8`KLXYG;lyg$Um$!|N4+{GtL+-_4| z?HCb*X7b*_%09cB@L1hhKCmcaB%f*~$K9u74X$$w>RLf{?7t(IJuL$?EZGEuu zRGk)XEOUGR^5+Bz;e8=>#0)%M5FVeWUB+**9(;WzF8M17=lw@d3+T5#!(s%#z6`&9 zS^Ia{Q&l-KVe2#B{~k)L!EQA&l*JilFsCwS)7-}qfgdz*$W3-;OK^hR!)5Eiznq&j zKk1$~`)>-R#2S3Vs48HrLbQ~uoob%VU5)}`)(-v*`qk&rtq0c&=R$DrP)xd17t6#b z8C*>Aa;*m7oI`|7(>(v0OEVm&M2ciY1F(RYBZl50w|%XCz`I?!O|t?w2*8_1?Cx}@ zR-J^_rodJw>YsgGI{&da^=ofZa&W%sfmzaB_a!)~ib8Cl^0+w^x?CC*WPeWcKj=>%-?KS?7l5h)45!p_f<|itM`F`-$N@g6z z>Ys!Ac(8_%x_bPs-H;|s*7bz6o#RsX@LQxFjm|@xv>t!qGPa5z9MtyO*+K`H+m}c_ zmFy+kx;Dbfnut$KWY z!N^r~^~=g@Cji%)8Zzk$TGr zIV^zE5w*Wew!R)axE?zBD1a~n0=^)(+{xCvHJrXq4ipG<4AdD;j;iQvA7N z8|ZXKVr3gNch%tc((@A-tI6}q4qLhtHJWNfWSkXxA341a#mvmW0?MzNU5UC+e+{DC zy}*|pd9BG`>7VeCjL^>9^3OvT9bJ9#tB`$h3&MppiQ9!Ab4-!4=X^%*=?b&$zN1UyLTP}rUjZ6X1BVKo#%5*@kn))%97ysCRweO z1i0k1P;auC@Gn%;SMuyiKpWU;g_8cqT>fVJz+(a6@1}=L9HL$Wt+6C-QXmO1qKv5? zcjj{JuiULH3%sH5%~sQ@a{*A{`&8cK-Uey@rR+G{MjIRd_G*+A)8fK*1+{n2WK{S~ za4owHl>z*Mc1VLAk$sxEtH z`@!aZl zw1*ra;*(8{T7rQYLGPbKXM4I=m7#K?5n(XY`(=W%WVdMY1a5j+CfLx1Z#Dha#Xt@< z(QpvfW%IY?a1Q)2yDKdJ)V`^_CGkX2@dgY5jtR;5JpU$cHraGx=TsK@z0mP;8mb0N zT89U&xntZl2Q~l0F1-$#7Dtz-LgPwIwzaSX+mMTVnr**+-ijm}q2xwhiua%HWa<9K zoM!tet722I3Dr<$j{O;bGd@6+U`zQmbQ}2|h3o>0I@?!X7OYG)=#b^QbZ)ny3-*UEm}kj(~oa)}3+YW;I?GrNS$&#a%}E_J8@>NMOddT4Jwk1eP%*Ae-! z1efnWUhz4aAD;UT$oF{g4wx1Spy!FHpUnhnImLH-puCdxY9df0&;#j=^md@CP>j`S ztfg|@`xj>p)oGYL?t#(N3FMl8@@&=84sw$Mf^rHToQRf zNn^P#4R$D{tZiAWN5F#T+M~ZqAurl-D6Grejt1W<^^F#ZL8*aRfZgw-(y>4zK;}1f z=SmajQ@MJ=Hu7)PpssY~%b(#aysN1#yNV*=-0%l-$OP_{;Y7PN`?Xs=8qsea*7w~C z^~ay#QJ@smG|BiGGy>BQq`N5+m*&|wjuJT=f6jhd$xXOH!D+neah2pf`RX^uwa2}n ztqvOReDSJXe|2}9v=&vm z=YG!R*T-(mmw65yF28O0>Lv&&r>iVKy1Yvp8Ki0tv2*>2g`YG@HR zM%)D9=uxo|fT7oiDKf>@aZ~UWO_1T0d-(tLvYp_k@c0_ZH}EDYtm*|p?9pLnS2sY(rXi}-QB5tyJBP_6r^0Ant+ zUqgU73vA!J5~J_Q%j?0nz#`_gaS8uXxcWc}wS0T^{=_FPZmOk}@wJ;`_=%JOGq+~& zs=!G%>Lm;BS%IS8C$7Uw(rD$i939+I%xe$xo-5ETMCV}g%)V&??QrKZsTvP%u&jCD z=0?K15l!)Dw4gu@%xJndf^eFY2%HUQkdSR=ANU-4k05l<8Cm&zZAtCOTaApD}{bk8n@b%`xii36go|s4~xc$Osro zE=%txiCaxjvJ2Lh{}~U*pKEssK56A-J1r6nMgK@ZwOA`G7bd|7Lj%f8iTw8dOkBpC zLD}JVU_Scly!EbP?DaO4H~veeEKEN5kkf-Htx4e^W4U81)Q6^erR(jTR4RDnqBqt* zt&@fMVQ^0FpX-UrlF|x8;+yhMsO}J^w3nBHbFK=IhX5-IlHDevMPy@z(+Qbn|M=** zE$SIUKZE0$90{B{bdK-e!+AIy6)HU;F!T(^dyipIe@uIlgzr4X{~p-G53Ury zk>{Pjpr24#UkLrkOb!AcVI|w#fHh1Bdly?yHuf&Ovxh!&r2~zpZPTG9g^*+CVe~w3 z|KeqCk6AKuO+IQ2G$LRMU-jf~9^-?_8@g0?Qp*@IVKDlB8%|??_>b*-s_Id2G>oy0 zV-j>%aho^cvtb+Z;PCe%S0J1QpgwVBj@_$5vCWsk7l-HRrqt9kQnOA49>ZdH$#TJ^ zK<`tYOJg*D;yJa(KX#66^5~5pRCqWDd?4RCC=qyf>RDDP*-iP6Cck9I;kF@KfzLwW zIhhl_B_n?$X2B38rz_`g3YC}T&*$U2d0a|m%G^|Ywvqbb9I%FpjKF|SeuVoodJMV) zHmwC$iU-=FR|tY7TuS;BPdJ6Rt7l;pOXP`f{=xMSWV0_0eMpWGwLpJ#q2AXV>Dz(r z`#NdwgQDV`lL@0H?U2W$}0MehM6IP-s68lwa6A@pw7G} z1EK_y-*G1#O}(;3YbrjaAWys6<^g_9`g+|+hidV!%K%c!!s4XgyiRSzckNE0P&Jda z3t}8<@UP|W00@rqKJ`Y3aD6F^X4HVGKAdMvgob1IJyj)~+tf$4b)-rtd~(>sH@lm=u@=3i3|_hcs2kVV_%MkX~ksia*)yKzxPc3e!RW)4{C=V zVN%hjb-EEsPnVc(Ee{6xa#$>q7^$>8dE1V{0Utapi?q4ut4&{|!eydZUHGFmNDVVi zrW#f$?$P5E^)*i+`t#BBkh9a}mlByZiPadiG`F9G*7UO(XBbT-#~izY4&Fta0_>g# zG`gKO!L#7JH=+Qe8xjuZN$dbJB!ry0>Imqb@$u2Ek+3^Mc;c-QWn?=7y!yu?^u6;?PK}ZiKq!Pi!1Yo9l}s6)bbv#h z3==FDi={qjO|D=T$b53tsedZGGPS>&w%R5%)Ck*}tpXW57z_w1Y_SbB{CJ}-29?4% zcV?Fm;1T3|c*_cGcz(I&L>lW|6JWk2T1DR4l9t#1_|f~an=#;^OLQd&{V(-tb*mMZ_W$$q~T8x|U9u}+x!`?G{Kl_RMx$f(_?#<>g>(aM?c5ST0 zgcHeDMSrvOP|Pg%%sFoy;OP8x0Mk7Rud3DPOMl>{#AJbudD6;fGc>7I-V5>=v89FatgdZ#IaZf*pXZA?S30j$7 zy-;hnbQHe0dRq?WFQsU5deRGXOA6=Q?|j&a>Kl%Al&pid!NqIetj=a@+Dmb_KPJWv zu@-dAInVBD@<)HyLXZ<&a(Uh#{;lrVB@yDVdvS=t zQ@Ll9si14yd9c6*cn93Zh=Y`m$^Cu&1d8uMZ?=qK&xITIwyXEWUTr0bu>=4-F8Ehi zcm!6sDvq4DX2mZ4$>H~&P<(tHBx*U94BoWI6{s%6aNFV|(i z&l}+BC)I&g=ck+yE6*@*E7-Y$HvA2fq;{)aP{rZbC(PQg5WI^HjWoQ0g{RreM`24f zQZY$BpDl2e*t6qsm^8`(@AsNjk-0Dq`+Y>AW8}PQR-Gcrv;%etj#Jjhf8M4Npx3Sz zV>!Aa!OsufXQ>nuAPQ-r7YIUVah%eE&E_c#xVvgDOb zJJD#vVM=vY)3^bVLyShVaH(ee^vKv|#ZN=CMv`5y8HuoVcKt9;`U9PhNa{BGRIP!G zpo2N)snPK2$xEHn0Fq+VXMuvkGo2MI$=u^^z%Wtmr!ya-&=A%o6*C|0;{!fv+ z7vGl&9vC9udlWsGguCfnng+#X8~+Vh@pK+awKejCr*A!)eqxE%4R4BJFoF@e=q_7@K84Q`@nud5 z+~syzx6eN0OP$BP`n*(%z`S#4G>%ss^&dkX2yDM~VT0qH9koES;qL|=sGco^lPb$g{a2sf9a+_>(jhlU?+#B8kuYu6G5T!L6l!FDR>e%!D`Y5B#nrIfGSx_ zt32wcW|I&%+pMX7mNdpL?UOJ2*_oDeA+RzF7a(j0ION}U>1DKI6-d!>5tiQr38o{| znt#o-?O(yLZI)Wm`$?MLfMp+lmQts@s{Mb`W%RYT6=X?yNG?zO1Bmt;`PR+(zF;Oh zuLwpJQn=MPzXnX48Ce+KrHUnK3UU=|MAlKkRtrRT)p-sfZFm;CR6p>sHcFu>eNi7K z%SYiSqMhw;{c01M?rE}Xh6jc*M7EF-ZmPfQ#S|}9)BY`{YC6+e%_1&wL=-`vT-uai2BISTaHvL|;asJ? zJB@y{V^JOk-m}pYU}3Sr0Q`xMwK;$3h`@kGaVQTx7PjpGWem=qPM9C8A1MY>B<>ah zW+i|w6BYW}V8%?kw|m@K_l3jD)rHm;O#^#jc=Vo~=9w7V{}-`PCGh|KjYy)Sw)dOZ zy{6I%aT$RZ2p;r2+XI*y(Wm+vhmg7SAHfpRh3}uYJm2eHy;9!L>z-72-NlLWJQSF2gr}v)i~%Y2zqFmt0L>x+8uYTCq8IabPwoQ0t!W6x zSTRSiekYq`<{+KO$UNwR_%oiIL)h(y{{`F!H?aCPD%|Cm@{wlAp zJ>RX7XZZ_v7Rj?$Z|C~Dd)yFg8KYW7YiZ>&#t;%Sdp{#VYd|4CaJ)lkJ2_O3MVIHx z_>;>J!GdSs*&UQ=CHB-7vFVU(Af5-$b}M zw|Pu+?F-k$FdPzDo~!ONTbn(_O6@m?>NhD(aL8q+0WyLMb&3;As_+f@67=j(J;qwN z)K;7FV*Yi)E)B?Du+^w0$`WS!rbg+<23S&@2XZ)hn6&IU?sQ+1)bIOis6+NXgMiLV zEN-<|V=&9U#CIwBST*E>EhU*Wyoq=L_K#1n#5ANJ-=ubo=7vXZ&OB_@D#^3OOM$F% zz@6xke#tDjHLuJ^j-B-l^=iR-rN*Fg!C8CQ@u+_I92~1aNzvBe^W=~el4p_&{^D4H z{Md@56`lGYcQX$Q*H24X0}QgZmNBT;Wa;n*M2*AKcDm*w+4l|Fa*C65d!=T_9a=jyS;bG74`FOj>xEIwG2(^@3Xjnu+ z0wrgBvF%!#VswTnfAy4tn#1qhvWm-B66i;ID@7nU>WFa6AL=`S0hDuWbk}u$M~=O2 ziI;;ICsIFzMRzU@d8qB|C*aH`evlWg^|&DNF80rXmwzQRWV2P`mPmr5P?A zXNs^4Q~YFinDwDu2^L{BfZhc&)0o+5JDdQ?z8M0n6v@g9vX2xwOTt{V#|@uX9nsp- zP1G2eQEUdj08g@~TS?u&Av6gJg-t$>lneS-Scz;`zSZ8^= zoC~7UwnT_8s?%}f_-sf2M*R{jbB;a$j_+#CuYV~|2?dlokonc6vPBa*PJ|Y$*)SH9 z7N&wKWn)0ZohZ6j=1<2YhXa4w(h{DQF+i!`w%&=c%=4yX-p(;YSjK$tc{O*4{clIiMt_uBcCtiPHY-KD_J`x2BkM%sOFj&X5FH11k0F| zM0cRMY=6M$ul$Q2S>lHdAo&_W^;Q3V*`4W3L|i+0-lIG~bp0O1wO?p;c9$LbNQV9c ziDvbbKC|>rmIXTT^N@&e<>xkI+eR?B5a07f8-uGs_e0VCX;a!U_sL+91>)nwSwLwf zpMNKwt{BRmDT}RKy+J_M3h=dC_GVioR6JoAN{wZ3p{ft*c zn^q+%=b`s&1~9Xss-wh@$DH2u7~JyT%#gr#@aA{;RH+y{$rLC0ULU@0|31&v19cai zx6ZY_0yA8eeuN1QKkoCShrZ!g=qIGh1{HsMtfi^2F{~bOwH?jyZOcKQ23kx_?i8=0kxHHLaI_baPgNgq>VP}{reCHN?3M|a*amy!7G0XlZw#2E6k_9`d}t=&F+;C$r6T?&Ql&<^_yUmL+$ z#g|RKMDmd8*%_UM1?okixo;Mi9~`KEh3U}GK=+HsaRB#zO?$Fb8#2@X>azlhg_2N) za6#_jJ};RbLp=`x4S%~9JKz03u&@CnA&!(*edG2`XB8yQcTc$CP~%So_X1#@`lYj` zDJNt)N_S-^aI*UfR<>-N2XxeFx;J1Q{EJopl`>jnoj4EMqrl)R8Yhgsd5})S zVBy~UE{arj(XC@N<{&*NQjFnjy{wK@T3FqkK76or`}1wcN$jl!PPdxB5vf)up?>u& zRDzvb3NNQ8`yz_*>(!59KF_}X@AhA-qD&1bI*jq2!Ybv~&9^^i)=I-ZFW3eo_L^kB z=pvG=MR3YwfXyI=q)(_-=(1itM-0>7TQB1WMiYk@PfRZ^~+?1_7TGhrkTs|9u!1C z`%_a>e3%>q{FOq$*-Ty4x-$n2n+&~d`)G0v1TyvLPDtU0enQx7r!0ZqoUhK)3Hgpd z*dr^gV6TffMVYw)8i{1U;a=T;!3`WHr#9>t6ov{H^?GwTu0(#3ry)@&R8k3IydhGR zJDUl+uNkddNkVOA>u>-FsDMIv=;817gLI(ik3LXgStt_LUq&2Rlvla~%~{=MPIv#V z)lF;pSDpio1|b~q#fAcF6#g*%Q#A|{-(`64WTVVnCK+gB^Csa$<79EU$zEb$1P3L2 z`<_Li%OK_WaX1(2zecD;{lkKF0HOb?Hq|V%{tocr-wG+t-!zP3iF&daFr&RH&v=MCu zAGyXBu34vD>S6;%+T{CyO^>wGQS_d1*&S_)z$FJK); zr|G;aCCDx7`eP?N@U;5g{IOYkUlMoC5eI9!rJX_#^#6b}AqTPbpv*ti2UvA9bF@A+wOXrPvZgZI zvtGBJ40LMpbXklg!q>de&sva?|Fv_Qs`f#G;y3z<%CwlTzMDcA1Za-ZT{+05T!~7J zFd7qZ4oL8M{ZH+Kc_Co3vU+-QG zwk^Mg9fw5NDN;h=;Z)RMyQSTZW*ik?*Ms`)M>vakpTB0xOL`zFyK=MZluzPB8$lzQ zV$G!nK>D(*%T$Nb+bRj59?y}dKBHMD{+N>V*=mU{#>@r)4{xUDCW*|c4b0IiX7B*0WlGK) zohuib+`J9zzo0)wYYL+|l74`0#SL&6zW;m!ogCgKX>Zg0D{w|3EZ}0JCiANeYrXR82Xvcx4hlWhIbKXfad({-;4}TVyX?#L?&!yb2UGL1VhbGtC8qhib`#mo1yc@ zu-C(TKz-*q81H^LL7$&TzIE$FlQO&z;?{u?XlSD*27Q1nDTCY@$b~Hoh;OtdAuH7n zet?Q=Anb=@`K)eU#IwA_DCzd*=^aR8zhsxtCODR`Mra%5wD)O+(N>MOfcSp;Z{0cw zv*v0jcTtt6I6`viDIrE^0#2fS?^9(P=bccMf1UK#XVe}Oyetw@#&mrQG=s9oZsova zZ%>{mN{WgPG{vFa>H~kLEO1MdYpYuBF@KOK&*a$rVSf{Iq8#II4dNdA_lM2Mt@%AN zp^$`+WTl@+5?k`uC@gTeT}vYRC(K@YuUW<}nMK0?Ir>UiMMv!8_Z}h;V*#Bd*j3_H zP8{2+`TMbvUH6NYzwwh#0c}BMARJ0RHrsaor{6vM@qu(@8hQzmm1eQK4;Fm}YJ0eB ztGHJDaen;c@sK+u$`YCe&`$paiBFon7D$l9{4B6Ti_Q;yh*7T={wna!>;w z_>IX_z>K}pJ}ON1rr1f_`ay_a8zo1o!#yyg4Vsgg+#p}*Du6FVo{6d*`A1*oyBTW1 zc{(T$RkYT&tlSKmCu%h3L19478+ABK*-`{7-tmLniSyw|6)epQjdJ1zNNa4`{8LLK z#_<5iKKHU9hI0VBq=i9QYvkvj!4E1801c$^I&1d}$j5UYgX1&Pr|6i*94#VtEudw1 zPr}Zm;09VbP_xBbCZKE_nT*(UwC}h;7BvGUbL+_o08xyk*@=lNdfkj9(+`@n=v7Q} z9a{O#pUk>S*wQEtGFy6NK1ipwZb`P_Ryh- z$mN|&8gGZdo-OZTuVT(K{FFsjxz4?^4#WPCX4KX7Sq;L`uee!en~8Ue=)Ft0?p z^5){|?-Bh>n%KP`Q^?J33zIppOJExOPY$~1gSnq?#nXiRI+Ea9OJ8y>9V*Zky-Qn| zCqj))16uJKPJ;YLJSbbi)= z-KoVRfMlR-O9^&{6EdYOR#Ks|wJiTkvMKWbHnXAJ(FZlQ-%qe=6)Q0)xQy%#jfC&P zi2;m%49p&oQPK31(gM(88TEzed=rX%3 z@y+-e9K5+~$=ldphTN!a8oUO`dA7C|$D+cFs8Roo^tXUi3uv|qnsm}Xa1sJTL53~P zvhn{JwPy}6YpEOHcGqShwBCfA^`ON>Tf&nJDcHz%A~^;N;EVDe2S;*uZcd=#W#Jm6 z(G#rIL0NaVtnj!OgJ2y|@LO*N5s9*ibme2&t7VB}Qy%%;;-7$<1_N8U&@V(>NTg3Y zb`~q0yV?aqNSWGq;Oj+})J^$C%Ks!u>$dz7eL`V|%RZqhUJE_|9OCEEjKJ(tzfdcx z-cKZNhGi65$}iCvsGPXCZIT$vP%82J^>6x5)^U3EvWdaK><_&jFcE2q(9RLuk}r)Y zGZq=-(1pm7;-v+`5<^tMjq7H>nf)&m5A*Z@jo8RQPt=_=U+nh)sXWC|36Ra$QlB&g zTklH!i+_Pad#jy1C9A3^4cY0Ohb1gN?Yo;RVTsl@Z$-ue(70uX;?YpU=vM(Tr;#F& z6KxCIf0(DVLQPR=v?U1^2m|fO{8xuV*Y=mhvO}NdK!qn*p8Bhu@gVJGuKReT(6-C` z23T;A9&L&<9pZ)8@+$;a^iw5xl_@D2N5ycqxd|M*7uv6Lp?rZ6ejE+ppCZ`%n#%%_hU_X|&}T9! zm#^562Se}2ZYGAz#&DC#wm%=fC=E9wY?JCyd1WeK$`tn=mHQn(QN~`avX!b?PH~rS z4|AgT@ywhgDH1SuCk&S4#cSM!_+e1(!Wp=#g;^O}UVwesb!Yt=KM*qIqJ}#5NT?4$ zGsGX+mv@*@^zF$a_8m3s`=lO~4tr&&=f`GXo+k{t%7gK{D?%r4Yj&}NS)*0oKlOHU zO-c}t{%Nfdvz&7}juO6m<7KmYj$|#Sv>QN{LNc1*@AGzcQs^Ilf5zS7W=i|XkaKP2 zo;K^8igu@x3ufKL=%+jt^bL-)19R!s}o;(U6R1LI?pZ z3EeEHH4$h9KbGOWz)&X}a%isUdNAy~?F8S19-@z(cs@8Rp6^PMczLN|yeYyabRmmM z80PN}ui9BWd7@PMMn&fJ^@~csv~Nd^6zB0dI^8m0jMI>J9#6!d2~D z9p?3o4nwh;U5Re5ec@x?ixrwvU;P;x^zt%8bX@m&2F_tWP}8A|7ansqZrUTCO7fQj zW&xWp$2^s*Cvo{vQ^<wX>iRfxbV{!q)+Tw@dq3`lJ#x?Ct@ zaXY2xn|~nO#tiyDLIA-6SJOVHb?oCv;og;<`nwz6`DD_r}Vt$y27Ou&_xeV8h-}oW$Z3mkWdFqX)h$!1t%dv!FsHap;?bbyXXcMJ- z30rN`YA?VaD`Xnib&nBdPj6xz1we$!c`^`a0oCsa=jeM0n&T+=Brb(iNYhy&*yZ?m zt1wpxE;eAio!je>meQ;H=|6{wa;E7XCiHMrOkm|OldG&AiKDC7ejUShy7_Es^05Vu zL3@68s_0Rm8G^|rV7(E%5`T%Mq<%X3nSAno_cd?NYr)Pi)3Nj#bo~26T`eiRLQ|Ep zL*shSCePlW>6t9dCIi^ZuYX2eS_ts>N$+%t$8D;M+GJup)8~*d_i5c!=S9;q511xpV8}_U zd==&~2|Fs-^RryWRFjV-3V#YFgZoG8CKQNR^PZoOJ~O6BBRev6IdT`H7Hp5gdE z)k^a3N5%TVpXdL7EMD?|cYv7T1r8+anm-qLf$fsmizD-C|HMN&y*2rk<xPEM?JOAr07k5v-Yy7zzoFBqQri#j9lh)Ug z@e=Z=Yo4vzb?aG^6{gtxZ*DZE&?z8Gezj-#6#Y$32gcTKQggZ^hs1T%Rd`HxEgS&cT>9VnX0CZC z^_QAX?L2r2FI0n)PJ8*CnguUryCk8p-3Yf$8L)__7+>0|UHlbc6Ou?it?TjiUkL5- zHK5$(G~gc#<|b^u1HG8PPPQJ6V6ey+j+`DCTjNnvxzmD-IcznZ5$x!vy^;8K%qF$g zGH2&&#^|LaO0Ip&u`}JA;U!3UR1!TEc~SnK!~Go6wKP@?wJ4Zjb_G?WG zLL0HZJjAR#{{%Gs(^nnQ*D4H+5S-Y~inqj#w+cB3&Zk&a`#^#LC@1>U z#3#2dhz`a*_prP7$|_V(ZOIMA!z9}15e_tx#g^oG)o`t|-tx=UYXCPm4hh5$>*FrN zIzbH90=~Ldx9SXiBrfZ>4VH%cbe%V+7VdloW{JngzjwV1jkVNRBi%0@>s%1^DO__M z!)Dlad-RRmdC*;MAo7Fl4KuZC!mmi3CjwdKhVO7rFpcLKr3oqIQQvk$Al+2`dnzSy zp7VU+n)X=!E8g(?>C(5aW5lhG;`qOvaNHptw2HDk#6Znhn=Q*!O4bII`Sru8>)^>N zNRtaxCf6x6hAS;Nt@$g3Q5F_n2>2n1M)^{J?kA?$-e{lfB=P#6wPX1SVl;(Hw7#jH zM*s~*T>j|CcMjK&?55xJ8j)gmf6Kyk2cqIDH!~LKszjOv3+kysY2SlxKutp9%PDUivMiYa~z3U|f#*3Js zI-tBYE3tAc3T8ln6MC(qGH#u*I{8`e081<8toge<#f8lb=1mse*+Oe(?|@ZfP_uF9 zy}lBhO4f$*54fk6%o}6*3)iC7?mLj^4hXD9-kJT5p*|xWZ^;W-ruHc}eYI(XO+m(7 zA~Dxqpr4b125H`IO41teHjQ>=6Hi5j#V;($tLS5+aW^=l)C|}h^%(0zTc;D5`J*d#om7*s$kTe@iTZO}6AI{U6uYBI>y+9fkBP zHaly`W5kq67ptBd`C-u&#F5EzhNGtpSJoCVj)*UUhEsnmC|4wCfo>$4`P$Z?Kii?z zM+R^Q)3vx}^e%aU?pmFYB*l&*^BQd3P+JHZcx_(p8geFsQlU;4^0wpCSC^~~`F%6O zkG@&fEY|7SaD=%S)EX6pWJV}rNFmc(pZiR%W@-L;MD3+(8?F*nq(mR9?0fDS7BTl4 z-?-DpolD*@Ay6xN<#6y&zOAnqj~T0us#L9o*VMXjN>JKWaDpB<_F<0E;9#0bFvI;7 z_rAd7?AOQ+7G+>cdHf(> zu--`_R(IX+dUb8Voj#7OM}|ijg@3F_Jh&HcJ@xN7rew&&oy5KNr{y5M6D$%Y$&&26 ziAl@U2lW0W9)O<#^M99x*+IT3Y;aozyqgzw{A%=~?uW39HQ>Yxcrogp>aqxPJ+|%T~ z&^=H5Q^ZBn9A}PzS%z0D=Wl+2H2!=D+0+k6?ViP3;^Hh(NeC>rInE!qRFV99$ns`2 z(iA7pg0Ey?GpTRRYw<`BqMBwSV^A)m-Wj3q8!4AqZpufLd(oW zqD%5$d8?Hhq3J*E6vX5iqu#1?sgdYRDs!XxaU`ZHA*ba^6;s=I6alCWqFzBM-i!Hr zGXs+&qe3_M>Z`bWhJyEi3Q;aFZ89Q+MvmFyuoQn@w;36eV0@#h#lkWF$Ap#zq3swm zP6Ly{%ZcU1r4w4^nl0dyVjU&Nr=Kren2-yjg01rY%3mXnL@vp39@Jf22MZycHz_Rr zVVMK8C3z|(N=)iW!SNb4GwE+LqKy%e#~kJeFSW%?Gz#^bII>R7meCfNyeKO1tDXVU z8RIk&q{REX@j(t=pKTqZRtr|6Y4iYAKfY;H?#d2-O37ke)*W)x9Zt6HQ@Z}Lc@E} zopNbBQ{#n$&**nd@VK~oOnNy3WB+PkA92^mC~kU2`y>w5J=Ncwp7{&T)oEm`EgSWh zB=8-{Z~0sAXk1~tTopNi#8w?-3h|AOlyIrW!O*cZ2QS79QueLe9r)e_2gV>qV9bc%4-o`t&bMDkjJ$Zhw8jF5p76nif zCFZndmUoF{Z6Ty%sW<}A8Jp+mKk;&euFg@1b~KG9j;`@K_}k-qJrbGoUPrFT4iLBoag zSI@K8-lozCt732G+-=V{4ORK%F7Dm&oY&p;yU7!h!?&RQN1prGLIvv`q&c&qQ>%Ml zeCN}FJ+S|3-|Wb^6&koiT@YigEv}gSQ>ZtY)7Z{-Sq*KxB$t2nTLR_&h zZ+GN#jBg_84E9eDM_LL$$5?Q~@A5y!Md2;{>5t5DC)VDkcu&`^kp3n^^VTH~CdfhS z^dpA4pMjh)1n2>_qpu{-q4wT+ZL18OpUgTlhJDY(C&x_)>7;A3 zG62K9-FMOZYT1=RgaqxUpOJxw#w#Qp{t8pCw#Y0!_vb!sEAHu*a~AQZ#(6*U^#hjS zRF5p(A{#-jJeMf_gn!`-L zZ4(5}5+}|EELuki#W7UlvqPWE7b=PVd)SDyFFqfs(7hcP`_d@rmeVrQ93>GPJ{ek8 z3)n?$X57T6<1@q7-KxkRsWiRtFJlALEpSEnoq-1Va+L~fZyg?VlM(>8C8ukplfa!r z!7A89*q4!^-tjhd%k`o9WzA~gPeQvL3h7Yp;oX9B(;Qv{#h27OK-rGQQ$Ove+BV~2 zU{+cV-y%p3>HWFz;d}J+wn+CTGt`i2oQ?M~a%$I>UA?;|r*|gcfUA|I#aqF)+A&Xl zrB-7wlW{ho!Vhx=dg)1?98ysx1x=>z5Mt<8X?^N*KX`J7ZABG_l45ca@ndc1x9&zf_p0;IBZ2RC&2WYLe111h zNxj&?;Z7gTkN5ADZAjbPC=MMyZJ?G8t1BCx?ucCbie3y6%%Zc-cHZP(q~P{iq5>_h zcl!Ztu}=4&yxi1yub_@|o0~hFxkx?KWHAt>~6KKCAjev-xf3?@Lz@GFM=6^|)!g5^sk4C?&O> z%(!OJD;rdN=SjI*naR#e%J<@fav{x(e?~*n6i_5UcC;PvNTg!a5@Yk-)ijt8b9*t% z=+35*D&jMyb|c~TI9#Q8AX4S)545H zS&uO_7hIDux`MMCjBT^yB0G5#2Y8k!1w4wsN*Xr#(D9_LDs7W_8qvQVwpbL}w*5vb zj+UGep`q(`ZySpEQQOUUX3n9{QP86_ZQ;qGSG%%@GEF?5eub zQZbNhI8ghe8KB(>%+~N$`*B^Rt_>8n<@cE|uG?F}%GC(?} zh>PB+)PU2$!oo8@d&vF0%cq9~d!c%J>lc3U(t$n3Z*Ua~JDlRrABWru*{$7RdoD7T zPehJI)=i*~Y0!FRD`njOEDOvKKCWbbbQnoY=+l^-b$Iyaz0JxWsxV%zbmA^#+L z*DcQ+XQ3O_MXTcz1|{f-QXAoPlvPNO}BUmj}o|$5fXj5 z2(FM zQ83iAZn5V5A!*eb9%;i>8|SJE5Y!;xBG!Q?qJgXkiaMbpFE6{&)n906)rqtp#P_Ppeb2-YI$gH>H zf-Y{=lqI0&(L`ijrGaG3&`~F!qPN^F&4SKczbbsY{IJmCbKd(V-x`OSLOW|<@2cT< zH0?Lr3ix)BB_tK}&QK)KMUBY`1i-B7^0a|vJD z%}Tl1+{R}D?C%&}sPct5hizZ(kO+-m9)nBQ-YNN4@};Ef!ZAZn$peUWQ+cV?Juf`c zLrdf2lY`sR(GIhCKX3)YfWpBNvaTLp+QV`L*ste^+9M*rEaDAsiZP8SB*=;1= zU?h7V+^o_03B?wMW=xRI<2g+=86b}opa%i;73R5oci~rlv>O2eei1&~G-ow`xUL~*^<#XHwg^se%t+;s& z$?=hmR{O8S7IO*ToC%4U+$#IN&=sz75p)0G1Lqaa!(b;K-9{wH044GyzzRhVYG>y` zwkQkAVNZUyjn+(C|pj13uXOZZBL{@poTkSIDglH*i$%)Cp>yxYsQYxhZZ2ERypEq}tAnj?JJIuz$|Zmg;RznB`t!kf zn$(G%7hYeuT}Op7G5!pevNc>>^8qu@z@37*206Qy-bCvDLKSk*TL#`Di(ZZaGEKIN zeGm#^w8Q-^t#AQm?=ONn;*-0k-Z-YD&_SQyFJqW*IIdMvyhU|^R@u>6F@)-IFIAcn zuFclxPsVN`%=nZ?)VX|d@Kwa$N zC-|9#D=1b4`ew5(%MJsod8R1l`Rv+vErDX$7NXst{V&~|K6+za@Sz;nAjb1U^p_R4 z#R5TB;kKxpi(j&*kj)@5fzBNam4A6(McxsvRXFx{awNFrXCubnq`$r26EMy@dXZQ# zYC{|mQ+x*;6)RQT;GXA~SbF;5e6SgcfIlqcPJDL1)AoK|b3rXFD}5z;shnkH?|s;n zwAS!v3$YHaBLX-xls?*(4_fq;9ZTqhU6QUokboWXA4jjRSq}F*t*Q{@whm^hss%8( zHMl6u(wKWO(*tZw_DwZrt=8hZ-WOVlm#E|YBl6}`lC(XNmC+p^<~_HKp*t9AX_Bbu zkq%y&wH?XfMBt4}+*ZbILQ!r+J5t=Jy}EK#`u9j-@ZLQMk+hi#?{B6tuio|DdQwoz zOiXceQIL?3F)_`%*!7^%3oQDTZwE5#^&saho%TA8`HkFfnsgYV_m&H{H+E zZnQZoHGQ2r8;wbfz!i|IM~|{k);nXHJ?0TP^Yph@!g93XtE{oXaTs3J^8-;^U9{t@ zM^EoQlU*S6-g~d1 z3Mie>q=a6+_`A>jC%)(MgcEl6>@IW8d1vO#yc7IJU6GQEp6t@4OO#5l<+LtcA_0iM zDmO@oXTG_gK!^vI-l*!xUtC=L-TwP)cA>YYXQ3`KA1p{}Uyce1dHb!w&dw>?N^yme zRY_KdiT>dgk1^uC|M&BMBk=#z2-s!hSX{dF@rjb0w2u4aMsnCk0COwgnq14L-SY^q z*H>MEdI`I= z9c1Ep8^X&@j)A_b34(1Oh{vOBhWFxVtE^!n8<^(7)cShYdt{;#vagDbk zTp*Z2hNOw#1Hey>7w}ItCIm^L#G%Dr#oGYy_9LLGDr%{^6wmN?jCOb`k!NhZn_2G3 zUc?y`>j!~Oq%)%L##A?ABrXGedl#jGi|xhY>s42DCQMRsE~`#$tQ#gS!R=pp_4&DOb*^N&f_-|AfX zBNfbk6~Hb}DZzVxt&G~sQBcdrUyozCqdJsfL|Nl0lD?JqPYQ22M=g$;QFtj zxDIRN`rpRnIhG7YY!cwO=MSDu-h@N>_`T1FxYL0d!G!93AHc5x&mX{jH~_pGML!({ z_z_Axa)6ck$Q*NG$w&Y`OWcFC)A}c_;^u^j+w&ry45FZ|ZVyfJin9mBM&F-C#9qmY zHn_hMUftcD0IjvW3pO?2rHV14tgp1X^=?(hKg(&j(3>GByM}3@U5{E@Xv<9?FZBl} zJ`rDZW(A9d#6v&2cz6j;eo-ea=zZ%k)|nr^5_lNbMr-2oxaN^fNukH^gKVNi;5h>z zDq}$p-<1}#T{c@5biZplE4=9%iL036G>uJ|`SBTA%~a4PT1wc~ebvrUx~yUj^LbQ^ zHarZ2hkwX&^AX3#)I19aV|3{zhX)u$;Ko;80Ku?jpMe0Vl=gQ&xu>Se)kz4!w7H}s zG!7mI<=y}6We@iV2Ode7(u|)?l1Ll3p#pO>sM^k++R& z`fn4)x$rR(PT7PyjYEV{noQ)uT}-|}ZEHi-Y)xZJsQCdd*EGA|k}gYw_`@hYxCU4? zgo`zhq&ISkU;vv^OgB}*Hp@~hy)>cFB=5LE+%wF_*aZ40%OIT$KpVNj+3mdW$Mjl| z4KHLNs#S!2%Ua0U$3||==DmI)eaTItYK3)@yk$(MS90NX(g~LrMy!k9dgG#7Jh_Uh z@oeF;!UUDtbmw>?|B_EczSZhhLQb}&2y8{AR+`3o56~0m4pY?kW_Q`WX2mYH2mQOm zFiB}<1jINX!bo5yP;Ld>L@OmDX$=I(jbAk67s?nF+~A_&1>0O_KSIaZ5t$t_n*|Cw z^D1wRR6^Hmp3<|hG#*V1<^TNkXkjv5wo7Xx0B0=ZEO>=GOgEQA|0n$)fs&79SWzp! zyUkT}1*k1G6WsQ{$%(|Ndv*V4yWqA$AJ}yiKNWY7scyl<9X#tz9tbV0sSbN#4iA9Z zuEh`jw!kRpX+K=LZP^}O_<6x@mKq))5es!Is$iGhG-6f+H4LrHsDV&q@MCgNRg&Lr)iN?ImXMbFI`9%ZQIuncperC4T$h9sBlf4A^ zg%k1bkK?EK2Y)an6G22a*7KGQ@wykx3VVDYVT>h{t@iwAS-SOKkIOisVJ4~$&jzx8 z-_;C1QKJ7KukrTWaE#ZD$8$TWc9^<7 z*S7l7Vjfif9nfGkXj3&R%Ke&7GXLW^_`6f?itsOMzb8U}w-g6ZbDoN^Gb&?rI1DLB zl;I_)jKmd*`tB|qXustvO*E_2uZUC4FX;yH!QpL|qt7uHE?Tuku<3TpFO_nv=$p+^ zX*^uFX^Y8+Hmjb}ptD_xJ#thx%?IVZrHI z-=8mXKnjmDcfLpQxcZp|Pf1+~S(`IobL1DMB`sHDuAl;3i5?J>F)lR;7cVZ0nn&); zOMqI{31r#_y1O5oT2WGJJzS-6CX9PLFsnW-A*r~Sg749_Roc5+UY|&*U88ZG zMrBkW2N2gJ7tOlm*o&HdB86=j?>9NMVgA0Ox#5=f!Z5%f&A=zhAy}cns4v2)PAgx) zp9V;NS$;il_Ny*okogMC#H|RQ-^mrmk% zfvx=Ntxc;~eK9j9+G!_V$KXR&cHT^V_>h-^5t{AF3z_8k zp9*>URh*}b?3H7Ya{G=zw zurseBPx7{la}tnf0y)5$kK6SZ|2dbWfRIUzG@CIIK(kha(O1OJ{fLJ|n?n!GmLv(i zyGq2;aX08HWk!P~zJS}rpnc7ExceG_wJoCeZNa>3t9iT8MJ8<5(m(`XK2oF%Nx>a* z6o!Ra>>%1hO`KY(iZFT3j933H<<#nTCC7lrRh$Rn;dNB=955}AME79se-!c$o z(_VkQ)}r*K2@WYah}N3KYCfzBhcAW~za6&_1{qr8Tx+^8UwSapCZ+SR1FQjMpC0W+d>$z< zW(kpuiG&_UFlk6k+7^mVprPA^i%N+SFtuh3Lb=0x1C4X1)tfXx!Q8x6wlN`X?VfQ^ zE7>6Mc%t$WjBomyC9J-}=3y}Ik24%PTP&(pwMs&@akcXnkcCyg&_mMXW;r7nlVby+ z(G2cWq)eSE`hmDIQME^shDIQ z4^Sw=UZK?F)r@-khC#8>u(}Hzx(~1QX8sO;SYL;i#ES3$T*a7rGnWK!i8IvAgu&3W z54bLvqsq+i5B^@CQV?}y05_}SHw;7G0u2*!npF*_s1S$MJrE-SGjOogc~Xx5s_=^S zeoR$l^Q}=3Bi@LiEqZ_b^SRpW<0yG>g zX%O9!)h4v6_u_}-4T|x zsCHcu561zS@YuyjaekmpVTJLmzw|);^PT){4HlTJ3cVOw=B=j9hX* ziutX;S*8=d>e%bk)8?hxgxR89+Qxi{In*XA&4Wp!2ZI{*mfTTVi9I_+--jhjnYui1 z(=_U>v){Z)4(C%}+o${1*`!@`e7$VXU-uI(1otWR%8E<_lK?p^wGmbRa8Rb1sPbkb zanoi*Yt>@E9o$abPP=l#hTpBd2QlT?8^7|*F3$eyJa}t6O^3*QRd?{er$%V$tG_>7 zk8y3<#%%AaJ-gyIKM(^dFJd*P>dTH;$U zB7ffX8c2xB`Q!X72=!)K4p!b*iLV9EUxHtPyJvX|M2MS+F^kgb&bh>&WUxkjEJEVa zj$oIc%fDWR3Anp-#14K<_-&`sPms6xP5rPqV?rU-e7{s_3#ux@MS%l*+y-K1cB9rE ztG~)uPqW!tM#=y3ePbLe$$oSic*1n=WGr~?GcJW@xLP@A*kEx@90H_6+Nw61v-abD za&m2aq0JH@s*%;*>b3bdn-Xw{H#4SxwrYq8O!)vkkMdNRrOz$F1PwW_#$f6`^)giyVPi+<=v@bY;NUbVG zp?Bd1T8a@7m#xGHoOV-Bped=ipw!K$M>=m`ctwf7?9)m!QD>s9?@p2X@CA)anNe2M z8M|Nk2oN=Wj^FcdeQ7ATvn*59E_q8>L6Lbf)#ptxRMx24k!04s=4S^cOHn#AuK$cn z${#8;@H8t_L-Ul(%vMB8N=a?UnBXXOUTz9$QPkAfKFT;N>xhMi&3c}K*Lx$}jwxc} zPaBx+&p+re3cq27ADl*>+f^&i4&Kf+cHGf9Lg(KE7$k#T^kOiX~N;)YIT)2*h{e?0v*Ax`M19yZVc=>?zlr-pI;0_ zN1rupi;eD)%NVh-v*#jW3x+tc$7LIsoJ-fw7q@b~Vi0O)<>Q%CX3%gjM8Bt8F4rO+ zNAXr%`D`}$#<%{~jysmPDkPr{_g>(ptH&y zAwdu@WWSJQD%jz1GbU^hrN6Tuid{}$%O%N3_Kw^0QrZ1dJH=3-b+fsCKrIp|(vy_k z;Ta74&|Q1q1gg!qxK$O$-iD>!lB;>)_?2?OB`6)=P?t%ZNd{PRC7e>_S6C6}N2Tmk zq-Vt(^3)mGx-vhbN{hqsE+SPsQ^G#8YNqnISg-Qj40|4$lduo3CY{kXL{}eWT3OW~(|z!Y)oI zh}}&gX{hzvcW7;7$HCC|wRL#C58suG_9vhf7kG-0D z?E!9~7leyXPiERAA%N8_RW70czXuLcrMzb55oX%@@eC>C`k2e?uiUJcO|JqC-s zO))90YNh0#3Q2;)71?AT!60_-_vv-DR%fxtj#LLvhCQ8NE;Rm`57fJkjm-ygC?SPB zfKtL20vr6HjMuVRLCoW{eYKrw^P5_Crpe~fP!Z`T9w8Z`NeG(9kKzJ8G`*6#Gvckj zjcUQf%hZ4v`;9I5S=!IIK?D33@!JXZxvy(UZ>njkEF&R3-J#mYO$idMzZD2 z82oa4TeC6YkK&l{&?hIand?|j1z7RRces4KP5AVv$eZ)8LHV2@weU!KGA)d161qyO zIG|=E-fdTnpLB&*E-=iL0ch<~f1KjQy#%=kR;C^Hk2q~ChE#C62sJTnPOHNJGx$5R z9W;cPE$ULdf9I}c>8y>eEewZ-Ql9||?Q9G23j{$h>(*{+5$1R?VpKv!tZMybddu~q zyHam07It~$1MB6W99i?i?H_Q?SkWlDW^l(0YKq?bh@T@pXNf(}I9%tTw$GMNUNA~C z|JH&w%yPMP*0B0&lZ2tv1nR9_sWGj=>y?ax(Nq^q8x?aMCl9gyxa_XX&BNd5^SiIj z`pC*&{U)Y0^WnMj6jIZ+EBX>!SDmH|5i^}AL+QC8;mGSbqo8NUSB1CU^Zc(V7X_46 z=oKR~hWP5(lYLY+RpL|+wtcjhblY{v<>|L?&LQdH3RPHossnFj@YDOk)n)%H%pB_X zrizp|_$q-5H1zn-&zmd$emyzGeGO&xPL(QAzs;%i8Tn7cihia^Nh9eC^gg;sSpN4P z#HVUbK;ZBVj03iXhjV-RUD7v>_rcI`z622t|LzrP1A$CuY9JR~$k<`E;0kHwp-gg1 z_M^14=^W;-$CDg5O-m7Y26Jju+-^H)z#opN!H)R4`pRd8@Goo_WKLH|?DEn_o?AIz zfwkt`eLGbu$jD)m*Lj{KF#dw8m1i~P+Vs14c64Wq_(Tf|6HqnEq3fWWm+>q)ycjG& zr{6mea+?kwR%WCEAS&fn$%!C5B`>Q1;6?t@<7}IiJntf^C9}7Ir}? zO|wN@Qujk-rG;fae<930F`LygjrCFS6~6;uLZ~;TzChhXVGbW!0|`q9h05wI$?1YS zd|H#DD$`0#AT=K4Hk_)Zo zF2r~%dwp^d0*)IZF*G5E%?3irVa#SOdPV9gwp_sV;t%rpeWy9+a!^_?%jLY`Y0;a` zlBw{LQKncjpWi~}(6hteP#={i0&6vpZHL11zHy(gx_*P3I0fx;HVPWJ!J16cp2Ohi zZbF3}AL^iB&IWc3HXDkwH@Z8}aqk&?dJHN0r%dkk zs&k~c9d*qXp`^`szp^k>2BFIvS)4+fNUG=6yw-Q}4hEA78gHI)x?nzT*lNQR%G1eg zV5?LUq`%wqLDJow)z{B|~&MKGJBg*J6y;)(8kw<~`emM(*S&!3p%iwO%sY`nKf zQ%tBIV=kso)Y2d$dui2d&*8w;Kg@}UedT=|s=s-h9V2_=Fg}}ZEF+*y>D9N8=J~_}@qJ91; z6?jh1&MTz1X1PGp#jGy+`)OY0{hJ&@D>W}S$7IV;tINp{$Bjp^tosDNplK-@pgY61 z?`nI5SsN^>Zrcfq;x^LFjF)GzAG4SJgv@Z}7!AqC+4Lztjmv&#it zR627zM$cPG?8_lDw#uUP8yYQeRcUwltM0J3K(XlZOQRh}_04hwPRLbg*1OM(3N{;z z6FWD>R&A09fkot%#Jd-`yEeEA?u1feYn~LzX04Lc|5UvSKf4Dzds2k?(irgx>b1dT z>$OI3hn9oKlz@OCR6B|>?Vv+#F*^imF)w-CP~h{vbT6#r3Xs}_7xo-_WC80Hv{JYU zwL(a`k~*9;2!{Gbcp!q)R>WwS@9_#ntByCJy#4q^hyfAX45JmL_+CTy2Xpg?QsrDn zHoGmtZZ0yDw(IDA>OALHK=SXYg{P3E)N=`Z4fxq?ytQ18I&f0Xc-K9)J2?oKk@d67 zbwYGJN2VGh0lg=pV=!;aq)%Vlk_|(;(b%Q*TR|jWq}-UYDD~COtY9xC<`&C+v?i9Z z>=WAREZ>71Ww%vLm{h7omDHZBl4QRFCxBBSG|xFSVTieTv5Q)y^ef{%ua);04hoTm z(6k0a^Rn9*`&E)pxadMu&mBPRKuMD-K8-1dy!#AkVQM7wRnVJLHl8>&OJMAM2JZGjqQo9zJ=C8TS@EPz!P2p?Y<<=OzNZRkXi5 zGjdEqvB&zg>{u?iO{bPk)bZ16ZmKHhLTL6di8+?~1!ZOT>FBOIO{W+DNd4lnTIBDvu*kZnm*mst(R4U40Kk$N%_)qb!I%^(F-sZmf@~7aiM9Jf3gHONsm?!pUdRK zml|F{UxNww*G1Re1yj&a=Zr9z$Wu5n_iipqvycg*xbW=Gxw4MYehd&hO{rb)*p~H? zYq81$@u2TXUa?4D2{}yT7pWjA2*ZwYR>iH~jC*YT9BEoMn^0=u;V8OcisAaYltx~ zQ%WLr8fZ2`@5$#c=2BM-1Y80fx-lY~oTY2TeOYu#PX862uF9XhG5~^x?;bx4e!nWK zB!l;yU(ob7j*cr2GHfwOx9n52*Zlhl8Rvi&FHS}=RmkfGF#^d@oWQP7KNw&=1-&^n z7_4W2PEaxW&T()&e!{|OqV>8srE5Ft#fALt1J|y;p{1{d&v@oP_@`vEwM!0o-{5hK zcvr`gtkqpp6|CJtK>YLCz9eA63s&T!%LlogIsYH5>K(7#zDwqRoKSQCF?T?U+46_N_37Q z*JagC8o<+0fU@#N|GKsEVTYp%Dy>{J+*4FoHp^sH`0Scx)PfVt@fWppj|;v1uO~C3 z8n`o2Q|L!8sIL6w<<&fC9>vU@8Qb}UcWP(cIZgsk=lrEYaTdNrrWMks^QECr-owI4 zE9v>7?;H+2MspP0PVLoeR#6(&bVv33&8X3d3ZM|rOIyBYSl{l~!KJo<(!*k&`b2<6 zIK3g#9Q?`@b`mt&0ELVRry!;^E~D(rVXBZ5y5_We-dHWE<<0&b;pnl@h$?Dazalxn zzN4w)XBg^DPDLy1Riagyev!cRx7FqGuh!9)MU5ZT-=_6#c_pobb+S0>XpZfcQag;= zL@d2rBRKn#+WYvW9%vzkR~rC$ZN*Gf^>~6XR!=k!eAbnh8!1kkLQ^ZRQ!k1MfZ7SG zMj1>jFx+jS3F9QxHo8J{{@lX(jJ&xbNT+2Kt!pmK#PMbm(M;x`%QmFyX2z@^8;|{}iu1Q?KIn=D? zc-$Sr?=4`sNH>-X(slV5w|CQNRg2cWgwEi0CmPD%Lt#pO$ikeunRxHqa1N{pIW+25 z|IO*zzR=4qBF2VG$STj23g8GmW8b&b56`62F>DuFSC1KTI#{sp02tL?8 zG0Et_jx4R?u8ntae4CJ=R~o~2xu z5FU86a%X`;)h=5@4LVbjI4koIAAQfvHx{=jlyim~KM63UDUYq0+98hPN znF~o^nZH+MF2CgI<}d;msvkOO;&P;9xcUIEF!-FukzdA!&-Tyj4Y4w&GONPP@wxCo z##HZDDXDCGw&oUZQI~X5TWf7D^YqF@?d8q~@a)Y?M|wUxiow(?-aHKfhpbptKJr%h z5@gK|Xlv1%qk6kEz?C@Og!EER>O8i7@|8KSBI#!SWseaUY|ui}eh^7s#1KyiFNb;_?DFJGB56nl>%Ox_6m3&5|D~Y-!{e@QqsC6zYY-?)%{7};1tp}3ey>0 zPusMLdUN*$>1Hi&6LkO-?GPLW#McS5=eJ203XHP(cTlV9-|Y@7E8W}G_Q^FS@WTnr z-FWM2pI%L(sYA28b2?Oe4f1a1q0+9+`G59t7PzFHb@U-Y5G-P`>F-@`;5tdjycOF= za~mD=%JC7*nP;hUt-AsL1&fzL<5+t&y)s0U9Yw;unir5o2!~ZbYn7SyI|}9%?m24` zi&I&aj?a)B)V=bDyyad&d+9p@kx;|if?>KYbqgl(V=+(O*+TQq`>>DJj0Jc_xLPs- z?aV^3zyD6_fato@xHe!eUc=f4BM{De!ioxUZQr9DLoUaMEaiY@n=Q*NO^tG_FDfi! zGv#xg^$WWi-2#srxX_t5GiH1^eRbpOOiMRORJ16uWYxC-STMR=bl}A^);AJ9k)AjE z+f|>LM+sC}a36Tj^8)d2B3T%#c6clzZbM=h64E5(J00eHTLkLn-D?F4I$XQm3AV;T zoTCN#Odyj)4L(izISWal;n;|h7Fs#6p3gEDT^n9!DP%c$lgvO?lV4YV;9PI~wMl1AGu z^s+S~vZg$Y(!PCtcBGXyqSOt>-ryMYj{&**N0;%Tm?!n=l}lQeNks<3vnnVJpj5Df z^Lq(S{EV+m8}LuCRUrZJ!sC$Cg_%P4gJy{HWJR3R?-ScO^GT1M;{IRdLG_%c$Uj^M z3}?!ke>Cf_VUny90zMYB<4KSG|HrYdXZxm8sWcbOBw6U@*NR%782%iUjm*w`IkK4j z#(H}3Dfc#9X#bXmWa9?4Uw=C6 z;HnN%%3;H|Kp+C;Z@6H8yYCwWa)Yn}@exNf(Luw3QK!#Ki~#dNElXle8_<7pfA5+=3Do5O+5j9G>@LA z94~*om*v=mkyzXhk>f%i)YNmME=_~fX|fZSt_11KpiTUU*CrP zMXwib4Uh@~lF#91-(889w@<_r75S-MF{39@TYnkMuTbH$f`I zN}`mZPTRCn?<0zo=e|mwpB23~p_Vu9yMEB#FC$??;`3VrPm)Li5-OD)OC#ZNVN@X@Y+&|n$0STe#51A z8)ar7yCqVik^S|Ox2lx3)d4YjkheSfhRoHkar`X89Cr^Q0Jy;oW(-yzP`{E~lga3r z5uw$77cNI`@O_SaIq%_*oK*Veba^L9{T z{fMSdN#wBd^XvV#bL&vceyLR4vlqug5uEMz8#((pi^-kmm(zqBk=9aj?Lfvf>3K z*KqI%pG9i)4e94m>$h1D0zOYogq_kTlnPBoU`@}n8PR$ugaAr6wZfGO{D^b2gzlmD zr*N!3#!E#j50)6|M5ctCODtS;?PJ&}*xSoHD8Kw+sMOSaU7~cKN2Q*aUp)RJ@=~sS z!0KVM*!zQrnO7_Y3w{JCn3-D5)vkg^v0stDPlPa z_=$wG4^XoX#q_#PD^|M9ikoU?>ZSI&blf*@j+;Tk1QNHobW!d#qNrYe^rlLuJ5KX{ zB1>g~3bgY6>E1;N!+5nndNOdlQ5!EyPxF7yRS-6*cdoU6sjCqe= zS5AnIa^BVad9OSgp^5N$s%4uSoAH}Ttznlu8NHyO9i*+rmB6;AIAr$s6yM^U4Ho{+ zrCOKiW6iC&FY+qcM3lCUeOw-Hu%xRCN_x;IUkUf69zEx#4kYGg>x2NId2~t`UgvZ5 zKSHNu$G?yu$}8x?gXN}R_j{>ho>TNKX80M!zkm$aqQmwl0)X8u#7o8E5d;0QZdq;H~Zi3J~>GmzV~YxCZ>?@yGR>;wo*A&?!<@L;|pJY_HT;o`Yu=RZn>_9)O4a#OnRLCHTt0_XN%X% ze-CJKh|fKvNlXD3r>`=VRoxZ?XXwOoIH>);+vhrQ$xc4&OPKAa|3@L2D#FlK-Y3>3 z6mjI^0w2$ifkgKskIjC@X|76o5w$uWu~9 zDEnc+=cFWS$lhlfI3f*;x|}qI30J~ekg-5ihkjcghd|XwuAZ6HIc9%acguthrff`Ww! zB^PR>3r0PdWRii z%bnQ+5if71wq3U{g&{g`{-Xu+!19HD+HzW7<7Hch(ZTQvA@h1vDpb^BrF{>>{SAX+XhPG43cGk0)X zzHI!C#jm8m9|A`S$k!;%iFWavUoV}nWo&aV%4{H=1i1G9@-z}7!FDBQN#V+S+g9ky zS)2pG2Lk0;p47gXpF49I5E@4I>N;_zUkKEfSXk6h<@nqBWYS%K@j9{ROXQS1eL1Pcez>b{(~_sKw8H>)}YKiQ3U?P7hNjP zdC#qv&qcX3BNhPF+f)D#l-|y%5K=Y`c9loQ>^5KB~yTL)Ma^_gWbQ(A9K8lK4`OKu3MU0`?C zZ>iV$aPOaTDXcm~mrUP~|2~xeQZP_AnOk8@H9RKf-pyxWC5=%w(LvE|VSGa}yKGOn zz$n8>^Dmzz3d&W$Hj)~7zLB4WGR|YwV}a&G)#?eGjY6#PO(>7FMyd`y`o0s|!58^} zJWu>dJX&etBNXPN8Mvr?7s!yEY^OgiFrn)Tgy%*CS;C^5aytZCnLqr;crT-Rn}k3> zP!VQ|J3KraDCQ}s-s|=zrI@)kRW{!T9=7i+A44t=BdIwzd|EMwXc=6-y1lCHP^p(= z2m=v<8p}wnIb*$YAPavL_&oLIm5z;hry2&^71E?~Cz&QeuIEiSUi=1>=$2K9J1x>C zUk#v&Ui|=tcca#~4YCcwHgY%E5_=+V)qj1IjD%Y}-psJ(0JS{nkJl$%AxHjZw8Bgo z_U}73*0Jqe)!`GB0!Osmd_-Mr?6Pw%T9D6}TR}8ggo&!YrH%(j;c!{}=o>C>IiPDA6 zN`b-qZtr{qoVa^y8V$^a!f1ezPC_Tuv)DVsb6Il>(9UwXSXCX{&+X0@0we;pdOvRx z=d}13JqnIW2X;Bmk@pQeIDH`2o^)hqJ{v_ow667vy1Yz4H8U>A_-_&%!M@Qnz8R9( zkad@RN3~G1pT>pn7+q%qY z6Hsn{2J%~CwMhgUvW>5V*XEXgCj7g)>GKV%KkCA>jH5kvs%?XSyU;o)DzrLTp;d1MgJDjLS_7n)%&~l^LJ8}j+X!|ktcM;_qTGz!28)eNIzIaEmR<6RcL1eSCa3fA{N^Wg8_JPt;0H>hAXZNJacs?ml~|1ub~ zWNjLIO}_okKb5kDoxBk~Deo+Bf{yx2w+fViRW?}*{6rn|+|Y*-Kp78vZJ;t24O(`z z1rs0uOZ&akOaCN>fcQu?0mTSsa~&|``8kH_ml&GkJu5umC)H`1D`e8I-uAytQR&6` z(7&NbxioU{n+7zk@w@`FHC28lXO<`a_wI&|^)(ZQAS`a4VNuo#8 zJw9$Q-LJny&obAD4h041+y9!-GFflx;`8L+gM~hi`0W`f{36=vyKn-r+AFU-WBq`T z`{a!I-_XqTwReEN%1~^Tjc#()^vT&H z7$s5jJ~4IKKx&;tAFm^bRHm7N3R>x;myr{F)+`JP)poP zD`VfILO@?m>?Xy9K%r+;3=Z{oE(&a6y{m`~l^}0Fqv|fvw6xH&PH3NEk&a$7hT}!n z^PHK0!w6SLtNnR_TM6M>cld*0+FPo9FxV#HkS%f>I1?D|j&syE74R`1$5brN4U!^! zI5Hr_U1LfT)W;WgGIAChj;(7iZyAP&8^-dKL!#l7jnUUkag<#Z&%d$chOo8mJBN7r zK60P$?g-E6c*mA_+hR~lO0oq{{@Mc8*?{jv_?(C&Zow^V?nh6&r5|mUe0~@8v91x* z3-c#ku0D zAA5Y4)_+fUW z?f764_i!isuK}?Xn9F1F=k;7*-P~GP(g6wRdkJ93xesG-sxx~L%enm4s|@lL5|@}R zh0+}}_>HW~d|6-BjtY)A>>WS*jHjh=)v8W~mfFJ{T6iX?%gxKd1Ut65HH6x~k}~4% z=}hQFAR(b56R6DPuO+P6&JDexnH652M=UZC>&_sK@kz55P+~0@6h6KGp?;XLHm`h7 zA67rc2qk()cVOkf+p5x_eO6BujfVy2Zc-Sc-@OX%R|^ZQnllX}J)@?R2d;xn`HlEK zW0%%0^aG2kt>Xp_<2^|o=v2PerHliklsL+(;#aZ#CN9PFo7W**@k50cP*W9|Z^P2u z>t=AAm~W=r<^7)l@8Y2H7Dd6nW1zQlIJ$h8Yt=1Ys9`J?}-#Mx-ob znJu-~;p=}TdA3AdrNTc;wAT)smg@w21RTKg;18!PU2Ncrgnl_k8lxb~&k~1srSb)t ztV)dtl2nPcT=q(U;eM(vQ`f2YbUm?csEs}JT zeAp#G|M)KHS!@Kw(BQ+p*U3wctNR?VMSk61>F7Ywx~@R=>9?`xxFc#Tw^_GF52el) zT|vH?Lw)MY*q<2ut&&02(RVh8WOosdfswW%&p)rbVPa7s?w-u<%_!?h+>5l1WO(3f zx?_rsxEfByHqG0NqOt?6Lx!iU_~|d#jlk zq=$7+QJ6g-O+INzbj2|iE3;w<;a}EU- z#zxU$`|WoKXqP|C-3t~c#oISZU5W@HfFO*g`YX znV8}O+n}EUmOX%zi^nnSsJB6Z#ul2-@P!Uy!T*Ai$fbrf`RZz|a7W98*mnV_WB>ux z6ouTmR6G9NeMm#SjGOZfjvQ?xUke@pK`|Ds+iO!K{f@TI1YT4lN~5~o*=)pOaY&y> zm_6r@JF|J0sq$u}F^pUV&QY||7#A&}qkRGuH^Ig7q_ zS}w8I?%+ht8Im(d3b6W{v);<8?~HRDb;cMUHOVL`nGl~bX+jPer!r<3UJO0g-r0rf zJXhdT#Xka*6CYic$8D@Hd;fY{{{FD|vzN2h-Tht*bI4sd@7CZfU5iyyOZQDZSLQC}L_O z^teJ~jXlveGkH|c?9Xb&u>OKW66c&-HfozPhu7VeQ;PAeX zjG0GsBYq+u1tWyrLm6Kwm9?Ti z%?z1`XdlXh-GV@i*PnDI1D=jU*ru0-4zNau#wf5I`)TWnWhn6uS3A*h?T(E^P?i2t zvB=s*8G}bWVUDs=KU&|wdr|c|4c(LnTs-rH<}&$)5PjiwXa(;HvfVfiM4r?;u?2r= zMG#|W)!m=-Rdh?^zOtgAb052+!h2z;CmBACvjSbdXSG#yS~2pjflnFdgBx?k^85O! zn}G6$9^dc;%HFz_tGcLGVyn))pEgXEi`?rkm=$=>LM>}ya))gIDeMR{Ne4A^h_|oN z2aTWRG~pBK8}>+G=(Ju`hRW~7pO4Wt^PWRowQZ;zha9({&iYSBTn<5#Bz#W%mK2`N zOB=JMpk)^Xld;~TO+vKMp`M|kqAbkY?+}Wii72L3 z6TU0@7;TNHY&JTH*U=n7K>C!0iK*>1l7N@GYODPcRo{3jA^m5qgwBoMLW?2J7E39f zU;kve*5RV`FY=wcK$~b=#q5)_>PNQL{uLx>JGI+AQqO z`anE%@l}d<;OoTAjhI#Ktq16alWz|rmHlV0W9$VN!HDzZbX)9Mer5K&9D+t`y0`n9 z3sR(in3~hN#ShI-R!kCG%Aa5O(xwJKW(4eI+C~Q!mAV(-lsZeX7CqQ;fXQGpV@Vi2GA6hdIdOvqL{)Y!9q5xwqn${~J3i z*M7i9Fao$MZ=|(N=wne-knE`hNSW{}&q3O&Zywj7)O;u^yoNsB?| zrkD6Vu=tIy*AYqDZ(W$lv3rW@QutVD4zG9nfQ~r%?QelNthwMn5kt_k7NySs9RjAB zoFM1GTlW1eEBOU7HLi}$MsXqunbDsatk-d@BGJ47RnYMr?Dn`8xu_ett*1`xT`{J| z)6)STCkJWhNY7;l*zHPAzMy});eYJ^TlkJK4#Y73Pq;;mDjr#VKtizybKlmfOW*hz zY(})>PW?1WhW*!hJnnviEx)(b^<~;}s8@ zNEVV|TGQv8v=fGH(Ko*xVMpROj{bT_3r+Ec`8+!~)|qptW#8vzF9Grkr+3f=CI}(+ zS3oU^a+-7@g7@X+R;gaoRY!Xp4%)1im&hpLpI#Nv6tza^=5W(VoHZWCoF%tE}_KA zNj=K39$EV|J0F+$c&zn!LRGIwj2E3Onb{qd?s-fNPHLE(F~LpIVa0;78g?0+qSAVkIsUw#e z-ZpZA7o=_-^=+|biI>!G=b*C4tW`WmzW^a6>vbU$hLZb9&Dr#t^HF21_!D|NhLJ1^ za)lx?!*uy~!R+7c#=2mstte(QW7P(tSQ5uvqWi8ts>QS+*@j3eRh}e$eOE0+f(;&2 zp>Q1~hvAr?n$VD*A2YDHVkrq*kW-*7bJO$w7h1m3NO)7}Dj!;j(05VK#_Pv?)lu~Z ziXpi-*c)-Ohya-u--$<3FVT-gW%--u@(RoZT~z(U`zA3h zn@+3z5l>{}dxq-n?8Sq`;;@-c7M`6jeRBQj{w7-}0tFmm7eu6Xm^$}f_A+&W1JkJa zK%7V?qp#Ww`RLP(8OH;9a9rYj_xU2FfPi*#^cnz}Oq&+J-!f-}5YI9IPn2Pb0SpUh zk1sn@+6?sEn*}{Zn0fuFWm12r&XBAX>U+Lrm^-PEVeZcF&TssarPtR8Fdn)UXyJCW z4XCK=7yvIh06fdTeuWFVni5V))jKo2?T;90Y(x&17e<43V)%n)FE=vF?7B{!OH&Nt z{AT_s7pF6uj2Rq>j`~F3l|XGyrgS1tg;Evr*M(5`ug}d#^TXFEq$2Tz1>((7A|d%X z$6Fb5l8MHBt53mof@1mi?GLMz2mPr_aqtUG@)YBBnW`0MD)(`SFA>=A;Jnr$1Jm$_ zOEL4X93wOfIXjrRNTOF(=cN>CU_tkUMCr&T}Hk@9UHSK4r7Zc553v=MUB zAiBz6Lga+^m>R<_qr0*j{o&I>96;*&$mVr&<*w~kPmeY(9UGD+eBJ)wTvGICfq+bb zOsj0dA5e4qgW++J9d+3a=eL=HW0FWtd~6QDqnvN6AuEN5zzQocDejpR|1X zUP-xcU&l#4)G{k3s+y_e=KO#ud|dXech0uo)vDIi{!%_S-OsDEfd!LU)a#4OqbqT{ z&Z{|qjgLHA=gzW)42V2uur@wahI?!64vlC+!bzX=40dC?ZtPGdBS?s& z-v3oLP)P6hQsFK^T=@rBR~CcS3GAy7X84T|@;}{&TUhq8R^y-J7qLlSA5IVOEO z;ts5N5qR}}*qI{$mbJ{RYQ?LJTqr(bD5h+9mss}Vw@ftg9rUU>BF$UbGw~Po-vEUY z<{IyXmv52|(vZS=%1InLkP9&*P@+T~il<6e2;`ZU5$7{oODOH15iHyzF zy~{+9BzdxI9#2)i4j8Qi183t3eRKh`;JuX5^Bb5k59JLPxFMZezUhP@W{-k5lhn%x z+?6Iv66YGB|LAKdOC=vVlRey&J5vZV2h)bbw)G zFBnn3_H1r!*3e7YVAcpx{YF~(9>9PJ2Hp9V%QQJmZRR+FfvB1#vO%rF9y`q!i=9nl zxXV64X(Aw?=$zo`C3PcQ{7?s;QMbL%OGqhNHGk#P80P2+#|^M_GwHdRmNc08FdkQA zCP=%s!N&o%JM)7(o;_447_Df5xhSm?EVP|c4i5y)S(I5#O1F@D$#!7-9_M-tJ*7J& z0h6rLx*hnb_p|q?hfE8@JwW%wH8tg^Wq?FGjp>)%gh+iQ;@Kj*b&lV9h`GPWsV;;1 z@5!>VL-w1x>^PQ+!xL!P;v+QZd-}>?^IQo(oA;ooPH+XMcQCSH2xM^=lNyE_&cw+m z-d*_oKCdN@5A6JPWluBVZ+?fNwc$m5E*Y#3wyjM?OWKpC>}^w%Xd5{DA@$U{S}&rj z{*^`MUnp^jy7w+=f?=6BW~#w({s!1L1vl4KEt!-o7CXiW&o)U@{AvG{n__9+sm09F z<+VfiY&mL~#28nZd}cP7wf&oghvZG3y4#{e{kl2hU43x{0j1l!jW?<*Xe$oxC>J&2tHxXNm_OU==rt<`*GbGqf;?S)e~)6o{aLCsd&rw;3Ttgc z%U0B7rXZQQj%eaQJ6_U|22w|ccDejGLf6NB+Bj|knpPLO4V2*8(O4=B;O_xR?tfI3 zMj(K;50EMUr#5%s_fL)P`hmIcpx()AzwVmpd@Y*x&yD|nA@KhHdgPy{0)P|#zaAOj zyNEgCy9X3OdWx#oHqQH47Me-_rg8yq*EYrVI*A#5xpU2(1`b2yXnwxSNmYe-hT&UA ztl?iMlg5-6eaF?5+Q~z`>MqEOEireZIT-NCJS`|Du8m19n^=eG$kzVU(RIUjZ=-#M zQ?vKsa{LCvR*$B@UNQZ#vk$3$e?y)9<+%o0v{g>-9l7`p-y+xZ66OBG8MbI+NfBF7 zX>-d8hYyh3#yvzTD+F)kQ%7ot^c^djba()250eP~5?g}hkxcbAzAei|cO zfYH{mqW4uq*J)UE_?)Kn%Nh<+DcGzHYqC-i9;tSw#-O$<>eaLmiV)v7b8u$@V2B0? z5S>Cm$lI_)GXCt@^5iJLj6MaM&79NRQnI1p8p0Fdo4Q`7` zo1Sbx6Dhp;YHE)P20EjXx4MB$`{FiR8@da833l`@QDr1(BU~|p)fiy`PVGT`dPg^L zM@vt;X;~kbtHLgTcHD;qfl5%ro2V3`9Isd}iG>gk67+6B9b}z(zkuObtFl=eV5h@w z6P{RM7%VhD8Vi+LN2Bw)<>;dM*`Aj6Xqd%Bc1Y(%@lRSXR?r8pd{GllaFwh|li4`C z2~5}sbW4T$1x#(-d?6I6?;;3dBBw#FW{RRl&Q=sTZaP<~RdMupj%`!2n__MNuh+zE z^DnN}eTl6yprO6qET5ZBUbKUKsQZRn`pEoL@_Lh75w>62d-U9VXTj_Ne!=o{#9vqaDj zi8?7lu3;h`z5GaM#GoCM4Ut@98x>bfiCOWaKU^);*V=b&b0o$x!|16 zUBT+zOU(7`W?&p2JeQ9ZTV5DM3})+_;6$bbeU2WP9qvil*M5hW^k87C;kqSw;F_&t zMvv(#YAl8=I&0dDwbzPI8$K_5tCUg*P@(@CCDQU;6HibWVRKS3Q zIb3@YAUp3$J&kGZ;yEmQbT2Td$2ncZ;E;XGC3&W36L#d)RB;Pj*bTcRrY>RU2~`j| z^YiXO4FtrD{7uUY#@JE!yErIl*MxlE0@6UhRt0i7K-}=}+F#%JuT$yYwf~<_{d+w9 zZ??t%=BaB1xwHY4!vj$oVBNf_z$Z5*E*{RDr&L>bcs#kLB&$~At)7dC%2YcR*8>zY@aD{2#7j>0F&pK;2BSbwRdeLp^_Qd!|8fAJXuc;BWIT*0WI)^#J9^m@JGa1T{1 z7Ai*qCZ`6_#_U-f!oY~g#Rc{&SPQCrEN~K7D-T657n40>^oNH z1lf79#EM(kT>g3KkQMXtB?U#ec8XF>yLc7%NvSiu=6WoVAiB=X(S*N|f5V8Y5{GX% ziVaDpx_FBLXo$+Y*}O;w^yE099j{>@HdNU~9060SS$6piFpwxV30nTOR>1TDgS$Q@ zj>a(8lliYn`~PS2Tpns!?VQ}n|HoL-O7k?_=+vj2^(epIX*gLa^J{SrKwuCIinkcx z2~t<)Z9j>dPoq%Oh#9S}`xv^UPo|+#)5HpVE1qst_{UZxKXtiwa(3VL=<&>1iK8y3 z;HCx5m$cc<;ywGP4CUI&rPp3-dXVR7o3s(fk)tVpL2~$bcmtt=yYux0 z&xcO*dY70=h|Bv+CR~+XvZ*_~V61sw*_v7usjx2Dk4V0^Z`D&J9d$HTvJYZ#x}GFF z64cfAmOysF-w~9>7}2AlcqO-P)9EZ$@nmHL>Evtjf&Y3M;gKLa|MV>NCQ7P>U868p z2WsUiGiO`vk)i&!mDuWrJpFK0qnYeCg_(h|5vOKAb|M4F9~;5bKuYazF*-Xf#?81> z)*3&VBn$`!FFD>XKH~@HyA?O$AE5{L?x3%lVcmq!p@&Cl=5Knc{+Y-lnIW4h$@ zOWREX*Pa4)Z=*?s0k><4Q?9V>WU|HNNK4k9Z7!{?K~I&lep^j@TWVDoH}}7@S{Zn- z0PdE`+%!XZDYvbUX3s`jdG*IgZMqlZwlm!jPWtszwKIu;VL1e-n5tDDMg%_|ZiWMf zDxr6M*p@|0gqcE-Uko+n{g+|+pk6Q>kG&p z?U@;J-vSoi3Q}h5(m(A#!F9?e1AFRtLeP%g-^gVbS_1cB%# zY0>D7F-)R!`kAyeFvC~Xq{#=BJ0HuucmB=+Sls?xv+SOInWwc zy}{|II#o%Ij+QTYbjiJne@iT}8n3ITy*`s{n>n^;%U#MEG)?Yq143_reTTTw<>VxW zW_=u>K6Rz9$If7c8C5xHG2iL0b#M}Y?CgotUL-ZdJJSVGqA^-EC-REA3>{6xy+d1y zW(w8&6_l4OPRJ2XCR`^ah^W#hA4f#<-S#w{a43ao2jEcpHk~88_#9o$S;6rwWG$^H z207!xo!vDOn{BF#@YQ6fwJ1S(5Iuhe6psf<^(o3)@i9sYw(8sIwJC)!ef< zuU`D=4sQ7G4*2k^oEwi3f>q0wdibT~T)_iQgPTw&*F>XXoX<0@qAH zjG+^?br}FsPs`f<`rcSjAR@sN!Zi*#LsD7{cjICok1tOH<+ga7R}606x-KRyX%?0G z{qBxDroF)|Yc7iPQFZFWk^YF6FQ3bB8ZSuaAwJj+=HZr*{Ph!Tv^2??Bc@z>I7rG( z@Vp-gpXdVmQl3M~8T?zsCi%c|abNGy|#!R$K3zEv!tRrIA=SNF6L4DvRr=LQ*O ze=2CG?M{f*Q*BdFeznqcn%Zar8zVW&GX#IkR}UWEQ=kFfis8{G7Y2Jx~*7{d-+(N>t% zCj15jCm~W+t5l%1sXYv@QJP&?x;s`7W`BqT1gm%k*f$ZFz4wUIrj&95_38(`KQesm zmLhd#CFYgP$QP$~f6rNf&1$<9JgyDkW)~(Rvn*oO*khGby)>FoJ8`O$x-L6E?xj~o zwy^s_caS8%BxFZH!7WFaT(zm0lo)J0V2sE~>~iZC&|Jw*D0?zxlzp<4K#^$19YxZ; z!cTkyw%B5vQZ^^bE!)Re*A;#(iiM>sKIP-G+qcMP{m0w`j^V^=5vupB@jusASTg9p z!_R;VDfA%VykY^f_is#LXH0+VpJR8cBgqK}Waj~SjQ?jmi#0R?9VSm$Natuj_pI0? z=1R~74cE9;mj~grZ)b-44^MlhpHWRwm1nG>&T5O1PY<&Ld?+z7k^w z&KFGR{a!IH&q(L2Uj|^aCOzFQxjmk`NQ!5G2~{^{4j=K+nGfZ&-3y#*jAedb|P~}J|*&fNcmvsvRt*KuT9ZE z?|8zo<9>O3HMf842Ysig8b#>QndhFN?Xj>de{YB$x6E*^rmHF~HwtE$o0%HYnJ5^I ze$uQgxC~8rHaNFeht(6I;KgK#bRG^1s$SkGir}Yd)AAH{DJB97e;)F0U`em?*rqbx z;?FquRJv^+B&}t-Pad=>{|B@rD#y`nrs@}=DTo3JsW6!YaATjFuNi9 z#A>GoqbT8i53ERHlen0hv!2GI$~E(AEkHYGKmAQEgY%d`WJ{v*RxcOWG)0pRL^1WY z(xl9kyj-e0Dp(oyd0Sy*>l0K}=gR012x{|BYY4}Ten3w=MyLRu>HN?|$ZFy{i}!B~ z#1yG=m8RG{bfU$itb95sru5lvdwRPKWQYFRIrZe`-Q?w(EAhf0WQ#ZcF1xM1w1A;7L6x8OXiYYs~A7E%Ub&0->qu}sEjz1J^?w^t?*yuj~k~WINxdoCQ zroFCzinzR0-({(MEIj*hqQN5VG6NEHCf+bP05NNL09Un|=^A7N_lT78$h*)Xbhl8$ zZ*K*fwq2;%qEu^i+G&Riim%w}8jKOigPAw4q0oyW)6pT)F8yc`;&7_K{poM3Z1vU^ z7!QTa?r&e1U@xdAXQDfpfqCb@OaP4`F z^c&_!6AC$&oV7DcbCS8UZWK;+o)NpKt)AG>U1p6qDB_c&2V3zj*DLg;eeKk=LmNs{ z7|I2dKaF;B*)HCFYDb^94gr#@7ot>5U9+`^$>#0jP~!Xwmza;?dGX3Z6?Y#raEgwD zL9=jwdPzEvNk=UDRn(>^LCPV#y84jkU3|U{OO~}(zlQ&YX~mD3rESTM$gi%-1l$S( zzX^qu`9PGGMq@3WEbVF43MR&hZNMkdLV`UvPhVEHMs8<<207rT$v)x!nCa4aN zHp381@E(-6*S*|2+xjUr$_ZV}l#uHNE!tDKs%|y^@y3O>GH8ZO;aPUhcNU%H@3>;Y zr3%Kkmdm;s8be~X!OD*LPY|svkyS|Mza}iUY$wQ{oWdo}(7D*+_0;60Ft~CY4aU|< zOjCu7(f#y7Sdy%to-UYE0VO`d-l!zvVSONXjv)+B)PTT)!UuYfWaCWJ?R& z6{|7yp5Gdo#Od1B^*nqAgp%J?1-}t@uc+=4d8a?`G_!0C+2!gQ65b(b7-T+1J7#r@ zv~q2=$;gdZ^|0&uM5a{Q6szZ)FJvDRD-f+YH|`Zo=Hp*58#_@Q!j~y#V)4_*&eiX$ zy1cZjYnOC}Rm(+(#)Ykvf8$e^oqkJ~RW|^ntA_rc11R4C)-jVXUk*q>N;|DFt~bs6 z2Bujm8g$`)j~j5yqcA?P#uxv$ROfY=@_(9(`foFIjj_ItP5(`9T$`x>B|!?v&NF}k ze6b0rMa|?MMhL)Q-$MhnWCAofdP=X&`4avmWmWw89`6Lzbf(VSE^&65nAdsa+>$nf zEc;*({2Hk<4;9>}zSDgouq}O|^$gdPba9Z)1V>OYBZp`KrT!*^HoU-ts7fnDgoEgI zIY!f<;*AxM zp5J#4!Kblap(+4-=?Q4D(XESQ3S~2*jgM0K5*<@NG>jLB?Sv-a%^?r8XCYIh+a^WO(2=bA?7Y-w5v-$=*-UA9m)%Tu`NqawurYSL=yZ7z zGD4ce!g~0`vnke5MCw>FJ$FV5eIcPFE&An+JhQkZ|HUf|ur`xhsI;HT=*Q@ysIW%I zqg`DL%1uTo0K>Y>N#a&R3!obcO>v$UE!xx`WU+y~H1akxG4VEIRYeMVVWzQYBXzhNYQ-i zv(A9+DN&*|bR<0-kx<|jtO>qZmUqxz8Xo=QxS_Ig-%s{MYnebpCV*%=V-NY%hTq^e z`6K6#+xy((s~;h-J=__`^b}w1 z_*fA`B#m&DmbcX@nm4zbM!5sRNu4S~LCWJ|tw52c5zh~?$;$p)+*T@nQLw_ zjJimOluYw{Uy^nqNipoq4Vg8?iMZHlKin*-&gc)y!8SyK)W^jFK{ejsV;!J90xLSP ze!aSB#8YnQ*4w|&F+EkDf*j=Mz?*MiXtCX~Cp%CZ%X2og9`|1b=&U#aMyeZjnqyaj zT5Az1x%eG0Qss*E@S+dj1sGG|-tjomXIKwD@O}>$IGFW97Ti;HjP=-;i7ZBiAsP+v z{@KFa?M?XI>brKVM8ZoX-tUp+V-&?`#mVzA&8!a=VDb$q@^vjPNA7%PtS*?sBAR?a zt(|8lWRdkK4VL@(w&(d{bVQxTST4H9)>v?>yY9LAxRBAIC?+HhVga69);jaHT+Zd+ z&CIoXBOzby$gSHFk?Ey_LW#N>BL>Q#s(fx*;wkqgP9FUI`NUn|X|wA7c;hgq)~^?D zWTUy2ZpJoCVUify0~&jesBm%FPsd`u-ygau@xU7<`;EM(%d$F8k5Rh<8Pg>%5(XPx zp6Ns-xp*lH-D+<;eQ3#=@JG4E`AQ)1FiX)CjqJa8jE;ggzbfwsE)3z4M3?oa<^w=F z>r?`~zQ`$8)T1zvZX)>5$nVPz_3!oolAwj5l81D6C&ShIsOR?)4eXWD9L3hU>`4y#3RmrUhDvX8a6T$aovoarAYwNBi0;X-?Fhp#fwu=swb-I;jT z8p|Mwvsw_elFSdzE^}&WI%j{`Dm4`Vn&f)fNse|Ny(Bv21FqMoEvSO*m^slEQ-@RL_yfj455IqO>i6m+3enV#0r2HF_vnxSU z(oM_G-dRTFLfa>`GyL7sJJy=y50|7Lh@+k!wwcUrNJ|$FCokgN_oP>C8;0^3WUs!U zS)To?UygzK5Kr(h3F=ne0m(-Ws2CPO$<+iYDBzrmrq!!h6gzl)tQ37*EH>GMfoy@j zja+OG-)>=*h{t+N;~187?_G+6^&cKbe1guAtJ@e^XrANrBc6g6JrA5X14hPZFsGUk zOUu2E4hg$CWfF z0m^D*aDnXuKleAZxM)b7ph25h5(l>0F3|h!{BzM5m5~7NJjq_vO4-UL#`qZX37eZc zGNUjHBoRWXi*-Vt-77TjlijUmL#m{OKB4E$7K^5{~1Ab<)9IgTVrWFStR@;F{Tj zDRJLJ%tc#T2O-pq+}ojBjp1ufu3SJD)rJcw|bj3dK=F@))IO892Mzug8 zX8`xgD*-1K$iok~Ks?Q;;ldKbs=Gj884voOUAX@Bhy3gK(ZT|HVXE#_o~xR@UzD3} z_tSig$uOcvy8U9HHe8ZRq7M?op*S=9S7YNDAAHX!!Q^Ff9&MXdif0gK?g>p)KWMyM=*q(D~d!pP~R;L#Xk|bmFHU91mny&c{s-Q(|Jd}nTgA7 z@xz+e4I9&odbK~@uxGW9i0uG8|4EKVLx_VIdq!#ABGTx}{K6Mue7U9$W#WpZt|=5H z;^=5(@k1tGLdu^1WW}dymuQ5JdG@!9A)Y!=k1we7;^jhbH8*M;rx5v z+cj9!-*@J4$9b$6$#XgHRv2jMHN{P~j;k&exiUg6yEeRS4duW{i$;Otb4%u9Oyap^ z?!=uWE9w+FS1711c@MpX@`1KHnDR8jA~o;;Ow}DoCiS=>A|W-4x|&d<;9adzFtZ=h zK@HuN@W}RB2bj?XiB1)Nx$u?NMf`cS3({pJ1?TjY(LWP4eX$h=E zhz&wMK5SyCdzFaWz{@;&wDorT&Bux+7;QoOQ&3NX{at^Yvo!ug)i>E37 z!DpQL=?b@|;bznwbetK^t-k@U+P{K)u<4Q7h!^I)Xn#U>T}GV9((6J?{Cs5WI`Wbj zJRan`A=HRxf`-$neZ+kPLu18D*og)Udr<{9k+vnLo_D>;Fr*zaDr_%)W+gbVdG;{} zsL&XVu9QrUY6d-{C1)$6p6lrkNGvA1{|fZnjx{Yj1!$^dpc3!T*423*-86$7x$**U zbY5Hvy==HU2#4w~2b=ma24Cv$wQhBBmANk@>Ldy}bjT}li60l&=k?W5KXGSNryeL2 zduhN3GojgPx4HL4pq8p&wjeQN!0rpZQj@V)8Q1Yg16!W@!OV?McL06atTJJEt;Djm zmFx}lcaJfMgCbGjNX8Mo#p8n<4^%G>E))d(gXQ`P`I*$f9#;otuhA|}bh>`a$qgc7 zg85-z8_uuwLAXL~-8PgrS02-wu2AkQUo3NfR_zLEK#UWG-cKwvJ+?6j0!JfPai5r# z(VXY`p`UQt+r#!6(j)dKQnVtei!>(3q~cA-ZO_Iq^`#&ii1%c57$~})LVqm{PEQG5 zHj4zc2RX%$^r&%>qqE;Z-`T>3g0%56UIVBUi%__bu<^ytnfF+$d`y+7-od1hs-qerMa*GsXW zS2RR?t!7rG*+?<-bnc9-yT{R0PiF%m)Z}c(n)AXGLwR@6t?whk{=Ur?J>_ukGoC;6 z^S^4!Jh$`y<)(NhTk-oPhO%k&@-r@I5m{QsrqWgXL+O*1%@p5yXEmvc!V(R8#xcyQ zoA!xDQgMfTH#xd782+e<<#G^e>F$qnQxvDV5XZnWebe>j)f6V|uz-9U#g|>`W<}it z)!VOF?mr}v59#t;nTLkY%4Uwom10&ZW}0Fk`4!vs4^57n@Z!aktWSFb6TUPTiwDxY zJ{XgTn}vk6Cny-CD?q|=pLo`%W+||zjR8O`>D?p6E8S7jYn{biM8YlAJ$kD<`gbK2 zRr=(K{b-sOhEyeNN%^h4UUimPwEOXG4A^&Cm;w+i1kWdcMu-jbIRW{$U@j&bH(Inr zSZ^F-2e#7BTqfS5bKX7GN64VhcbbOc8mhvHnS!!*qC_3WrDH&j1Ke=}U@Twp>Cm%% zGHH8 zTy44L_H10kR_Zlo_|yd>=2F6mez*uWaA93h5{8?^dJ3 z+0#Y^BE@&EFC=*Hdo06+lBw~^3`Z;x^24GW$#^Z!;W6jO6|osHl}K(}aft2|SB4|k zba4`cIZZ{W;YUXB>@Dy>6Z?tb&$MrFA=jdA4JAwfUrzXhhw{htl5!k`?WYnngeHWl z<-7HeG8f2gNFz$K2XBC4KvliZykAtk^FeOGj|L)1gr17;5UD&m#mTqGZ&_CIqBkTQ zIIPIYygSQ23F^&gGJ-7}qr3szz@XVPxF)k{S!t>BejHQcS0{`HuFsg-m83&-9o5#Q zSW?#Sj3Yt4>K#EneAaSO?TSn8H_OUOaEqD#N4pF5&0}alV2^Ye`jW8v;gilaLg*6N zk$_(RkNY=(B#Ey&)z*?&;J}Fedf$(5&M1w1Qh-D@_XTuj!e3bT-Cc)nVmQipprUll z{-l6(hUUnxW>E@T5DKBM_k^_EKI*}+U%+ZG$O_9!K<0o(o05Bzd}^Ny9%^T$a+6EP zOPPa&f;|Jt(4zUVqrw^T)q3(TvUjRNPU%rED)6Ep%Hz+fJ|1@zBT6-9&ldAS2-{jm zJ-N>XauUkxV?>2$tBZ$%X!~c^Be73m* z;Y#3j?*96Q{taIPY!j>QlJe425jiwkoTi>x_kEJh^Zm!9m$Jv};izv*oxGhZ6LmO1GSIPq3XFmY;aRFfR z0N+z=*xo@oYG=F$yAySyncx^|H6XF1#@XPiWp57;9Kfl%od&J~yRy!Zw|B zu3myc1Ij%VU_iO{KR|AD0GdsFgR$RkXOlo1&*4F)U<>{r`myZHl}5I}3`Z!G^uRXm zw6RyhV8O%eynF=T9?37w^Wp_5yb2WMu5urq7oSfivhtBDiVqUc~yC{%3=G- zr=nx_^a!moa!a=3=pE0(Z^Mhgq*$1Oc=GXmCm>UOuS&(+442~Vt3DY|MpLPqz2tT* zE3=AcHM8>CZ~@t!-$U23-{qOXc|7G0syEdhZPY#{p&$htvzQ=^%1%AOia*R$PCiMn zRlHBEYrJvS{GL=^Vd)lZ)9;RZE_<}hi!3Cwt%-h?y8MR%Gbf&DhHND26L+*wh>9WX-wl;vRgnrcCS>o{&9C5r7&?tgu5)}Op@_)Z5vt04w;|6m8>@EfgC-aI z_;Bm?4n_J;&$=;tQQ*{uj!_dAKY1OJHQ^mGFC7Lnb;fFyC~XWn=l{;AByko*ZzM-x zdStu=?$0>`s#;_AlhDVxdx;|OO6CGC%~&J6phFVFs5j6|rfU%x1Q#Tqwj-^my72e# zNOAS7Ue}p*UmW?y#vxiYs@4azWnJlv2vjzBR}e9lSRoIbjimz{TR@R}0GdzNL04R@ z>CD>?4xMnbUQ^5aZL+Wv&JU!enYY-bzn`W-@~1&&n9>~P2ou14@trAfX_zSx`Uw=O z=OeY(qe@THfQH2e%b8KajyqXHnPuYj*%!Mo^erc++9_{ogDtTCkS--R{&h>X{5ANx zvEqORBYbfk;J`>IZU#`YYkp7!x<}aM6?zpe_~7$Lo427gy99P2oN99pi-dC=j)in? zpUxWvehK{{XqgMfy~Qp!oq$+|?-z-t+_a|YbGqBakzh!3EU1?w(U6#9)3aB*<||S>>0D+mDzf&g^NfX$Uif7@c_5cPEaDpcixBYKL!u@e?7OnQ!bthEW?DQs~Y0crjI z0sL*oR}p880o-{+jM7za?U`1nR6YjAkNs<3ZJVG|5@|4so=l=-6e z=Oza+*fW?Ia{Iagsx}7YL#;-_VHx0k7hSuzbteL47Z*nWo>0J|fW{e#69Hbt8-NCl zo-q~p5J0stq8B>UP0Oa2=9(X09`iyZ!aYO7)j^zv1lFzyKE`N@<;VgU!}{ z<@r>6%j-!#v8OM@pJRTIA1Pw)s4I^WMstMd#+K$qUa1)R^TizZD}c||m0d5Kg}_6_ z&6H8LDtQ28xf~dbau`#;7W$Whq|eEv-{yVdq>y&}EP>_X*IQ&|oPgf-*a% zSSk4}t)#W^uD#d*88I@=(X_OdG4pb99irN0>>Tk2GKZQxco)$k)uz^jx|kHB5)3D(H=cMt{Q+0&C4nw7lwkMT!amM`<>whlrQ&?< zS%Qt}+VKZbn8Y~};C8OS5i1u@T0cAS5wzX=7a+<8!0UiN>ZauX-Q<=SxcjUSDB=-s z{f{2-SHm+gWL3>Zxnlt$gg}pXpiSF?F!`OA`*!{KE51#z=@s98(tm&-MR_&30vV$( F{}%+~Znyvd literal 0 HcmV?d00001 diff --git a/figures/ft232h_ready.png b/figures/ft232h_ready.png new file mode 100644 index 0000000000000000000000000000000000000000..e096fd56a4a827c53eda509ebafaeae72829ea51 GIT binary patch literal 9532 zcmV-CCBxc@P)}aqm001P@Nkl&q?<5p1!3La^lk8^ZX5poUDf3FK zLa4Y9tLv9~DZxK?&uIw|j_u4dnKwZORcvFB6@7HNPj{dG_Gxmyvd3>^&Aw(|v#;6L z>}&Ql`y*z5oz%2nR+Zf}&6Q=hZF|i=J>LIwRoLIyrpNvxyiI^xean8$y*>ZqU+3=$ zIUt1m1AhLX67I3FPwk#N|DD-f1XlX?9KOS=AKw4-kGJ{rAKw3X{zKtBzmzO^{Lgzn z-NJAAFS7sfOAgupAn+@H{zvr3^Pe`c2Dt1?Yxm#S_xspYfO#MKB0HV~?76o*{zdM* z@Y^rHzeRRd=9ky*53(b-`pZ4+XH#~S{|$Z;F!vwi$Mf^wSy3Xtcy@;qet(-d4;(vy zFPh!Q{tlXV>p6Q_@ZS1^KNsHuDSNGfuS0gXR^dYl82kIsqS=7ue6$(A|Fib%Gtqx31m6DgFanHRk7Yu7mW-yo&fi zwEMm6h1APLVym+0vFG0Y{tLRhEiCafyStAdpnm@TGG93AB@)}V$6k0J;?EcC&U$&j zv8oVIKYzck19nM@dF;wR&Xs?iFW8;@0FUBd%EcyL zMxjYpQYjn1vm{m-s}@ z#^FY|9_MHp-rel3kMo&)BBPM1)Ui#o0r$*JE{OjWsueqvD=p6*Ci{nn2Sn%|LnQxt z;gn*vQs~Nc!H5|<4GB&WZh&2_irY{PSTVh~}-4}E78J6DBnRS^u~YB07HcnXaD zN&S14d9WzoBeav~Ykf_X>&Q+={xZLx%14TOp^@BZ!>>mQkuOJB^ygRG?#G+4elC2N zz686&a|AH?gZ~s@uN-!F48gIQ=~qWeMse8%<9o70$rL`2&}6%dzAmdWcGJ(E@0+`3 zR~}UrGm#L%7i4p$=szW7_w|0ox~|$2&~Vs>F7hMttWJDxgRgX;%+sFGD_zKHM$tv# zKPBl{4c&ZCc6joDFWcL7#;$Oa$%y!?;ja0yvxUGQ|Pt>F2jjS zQScSofe7Bv6l>ri=FX0X+R*A*Jt*^xy-kD(9O^>7_~z_TGT|Q6M{z0Kmc%0wd|lpu z*P%gc+~2jkCji}+By{tRl)dnuLWCyFYhi&HgW0=S)@yN7#Og6SWP@mz6;ohn3Fc z>?N{Wrn#q!**ex|fB#2d^PdWJo4!-HWC7SU=+Hs)p6p_~L6ahO z8y4C1zF4XhGN*?ooWkeeD&p>U?AuTaY)-bXr*`MAy*7XDp!=yDZ}2mAp-XO`5;EBV zbr|Zq*}dx&!tK#kDvtvpeG@G2ow86ZzPa5cZvY#C?{OT3i@Z;hx}? z@NRs zbDjR{=i7_y`2>7hW_DUCSI}o)F=~i{`$bXSN zAAbG?b~L+|TVD21rO4cr*;-^T&)Eyf=Y_l2qi_7|c+zcOx8&>snjO?Zx7my0j9rp` zQcxJhFS5r=Q}zN1i|9|VsP+!_B`))PC5hD?9NYqX@#Wm^(Ko;zQ*W_9PduNq3on2@ z`uYy`Wp3kWvRrg4yvxp330BIL{W-go_$eONb5g|5{^kwz*{1AL>}p^7NlN(G(eY#7 zZmIiqk+N21rv7#2=gXBMYv4WhaN(!;w)p_NCdJHOrJuruIXjQo1^9NTE_3^y^4hq4 zw|@&qtGLjJsn-0u0GZ?Q+TIXgbov-l>6j+S~1%U84)iyxG=m;Qbo__}!bbGs`) z_}E#Qm;CJX`Rf3T-&42y^X1B(zdsMYE-r0q6Suo275x@x|6+nJ1Nf|M(sHb;Zy7Htyl? z+Z6qM8*Zz8aBo;^nc_C#TedkA{p`_MH)ZcL_Fvg=$=MmR@jKyPuH&?8_TaJK;iBmm zFDnIv{dZ|akMNp(&Aw(|v#;6L?2niIy|$u94%l^bC687d*soU8>;(zfACzxHKw>#Z z-+#3e@2l`%V5h0q+PfF4$a<10q^XVj zFE`a1kmCBNBNbNv)nVP?tHa`m1dP38KcCv&Y@yxtiTei>mi8%pk7gh&zncRi>wLe{ z)yM6Ruo|*?ortrwVH_-<6%1;ndbHYD7WVxI*y#fMo2lI~(q0lJQ^FT9Sk_t)bbsBeAboP;cKV^#i6#JNS`LQaxqu~H@y1t`wozl46<8keC8de8JE$3=5nK;!^CI@LSRB5X-=)a0D2YU(T74Nru z;V$-*#8y6WA_|jUV!N3TH%203hXf#rj0!8np}vQ@)XcF~0G>i(=QJ+0afxJ$x9n4? zWRI4DJ!+O(57hVQR5f3FUb0W7?0V^4vjuBIPc{W(57ze^uH9i(??4qwDzFPideX$w z5z(D8YvbB{Un>rI9T$n3xusy2FT>^o?ELgDYj56|Z%mJU+dMsK5&eYfvCAGcdv1Nt z9H8Ba4D9XBg?=qTB6}UmeA{J*m9CKLKyDTPOVgL5jf?F|+YhjBDKoo+o^WdS!p`f1 z7Ac=_kgdXu_IeRyPu4(HQfsNm-qH@GHU(b?Nj|!6-=>qqcpPB(jr(sHne*}wbP zxjdP(zcHum)puvt?Jem9^K@iN4Q%7MUdbH|*BLuG0(L0gHn4|X_Hw8X4qhHZvk-{B z4L2LL)=u8MGj)%hFVzqAl6V51?P8-TLEq*|ACibE$}I)UJmm4xfxU+-y7``4<~v;; z7S#VD;;&CvU*0AJ}UPGCf>pbn#>>ymP^;z*sx0^deS^@YQSRz}KL2dmV05 zo7sKRbo;cMR;$y3r=wjjOMU;d>_@#fX3Jt@8EU$@d}TJD>{)r?(`ZAjmgBdf{&*p4;(pJNX# zsW|SNu$PN3R+oKyb=l+LY3E)d?$TEG5AixohKj2R@Bl68HZ+FY3Vj)cs1+pX#L%{g ziJ!I=4Dd-(VA-i2fv%ZdrT(xW45VcTfRblL0vXvSg5X?i@;$Bd>v0X7M z`B2Z&_Ir?)GNH?-JN00(qpAOoR|JfRW#^C%jCv$K%Wn=7Sv59vdwnvdot!8_Gaoi9 zrq=0q;7><#e_);>yHt`JvEZ^3$r?;-gX>95>WO|XrCC}5*pt#$tMp1)es&05L_TK^b#i^((ZTKzfK61zSOUsCEc`vv zFuA9}?0%%AAYrwh)K$5$^O2;RB}nvq)yu?T@tvbPFP5J@bV;XS2ChFZ^K!0lI+g($ zMOl4^IYtMj8Tm0xRU99PGNveg8vrdg_wg z#r`qUjRHGnT&{``#TlE{hm0d)CSM}Ih9!2oA~#c&s-ndvEnHggA_R7C0(-awZCpMG zv5Q!LZ#I7SvyY*M8$7G;w%8USm#|>yy34*9jeaCsa%;y2z4c>+mKn7)ptYk*xS1~Q_9q%U|iX5C~jH-pG`E^~CV632wy$gZK?H&tYpjv?3Xj!JgalZ@R<_eZe{xsgbii8!!V z4UlG(4t4oeI$-RiI-OAvnpKv774nM94c$gP4a*^|iy?C?@(P4vZH1(Ava@$&z3RNe zmaiXG^`fHN(6@2q@gT75u)RnUR!YBwfmtl4V?)5Paoo?)>%rW%u!F_{{vM4Jz8z`D z*}^zNoI=mE6j*Ulv!EJ$kUY zy6o}!B6*lprsdlwC`>OMh8%a{1Zx>7s4IEn5wKH8{8XMc_VNvGxU@KwmV2eHFm9z`OQ>m? zcOXdVB>M_0^GC2Zp3o}YfC91PeC$gh8ohzKwHmXDDn07(4TI$ezWW@gh#_CWP+= zlASBT_E&EEgkagmh+5)tLy9B21a_yj9EfYTq$t>3%AgSSX(M9CZn8Of7?-TrFzm#` zVj$VcwrzW>1Z4n|KaNPPzdarpz}~U3FS9cl;97!76ZTsiGha8Ed=zfvaer8|hy1u% zgPlQx$xf~Qw2Ov4_x%GB%|nDz+>@n4TouWp<+_9C^8)80DnoUxN`zw&qI zLeQ_>B4jR0=2>U?XTk)>kSoB|A^(gI+|wc7?hR6MJ9#8k?iz#?{wDTBV#==h--pm$ zIb>d`>|Kmp$M$H+Y*O7sK^?53WjBG5%9NYjJqL6I*3j}csu^&i*WCiWB+97XMfSF* zLq6D5j{1GjAoG;g{`5)ouI;q5aP^q-)$Ba#iWM(ZIWr$)7rL?o zOXD%^hdYReC`FG>Y}gQ)R4X{F;c9qiYys(@-`r_ND%Gnp{}EGaRYedfv~jMlBxWF} zMP(yZu_`Y@*Or!R?Sc-uB%f|i+368eYo#X&+4jM+xK2yW+rBoL6JU-x;&= z*q_ur_FYxJpdCfNn6krYUV=eAY!44*{aD3ag=bcIQWpl}cWhZaZk=t~lEoz1-O`|) zf%TQ4fm565kdb{srzAMY&e$gtQYKhyd@7?<(>TNrs)Rdw(xG&8J>)wU!dC zD8OD70sB}D-OPfuaI3(M55tXCa1q81*^8AYbsB3h`7Q5MKEZAWkU|8d_+r>PKz6Ql zsWTn2f?E9DTRLPefx9)+cufHVVAvb7X_)X<&5Gwpq>>B4>;Q;d@N280L~Rhf@B0>H zetnFZn!ICAwqVw!V-JZ5`8|u5?o=h%|l)u&PyrX36yA7cSu^Ur@YzV#5h$-q^c76bxrH?Hvo`S`SoXh^7aI47f zt|Q<-S-3WQD%pbd!!EmlI^?daL*Dgt$S*%?V7WbM(6NanHEQQ1XBFC(4cP#8tYXhDAfU#NYB?x} z5Ld(c&eAS@J^NtBK4k0=l$yBgO;{x3V<+h$)^|1+$D-0M`;f5tZu;xH zi0t5LAd3iqft#s~2$p#R*&z}R6`z{mXlpqWTcEG6?_|dwH;JEJ?rk~R39y^!=XVVB z^W^xGJ|Y>&20_Oa%;I&W4OHyIF;D)&?G*@h=6dA|U98%TDLX0QLo9TC>|L^}l*F`O zhYakY9;ikE&0eW`QgiZQ*sNiM z34uz{E|Jd+f$5MF!bNqaL%y8rkbxB0OA1UBVUZC&K#mc3TzaHQk8G4Q!<;WZQi@+E zB5yaozJ0$VUPrx=itOH*`-9EM`^Fy9!k~jU2P)q=W9?p5tc}&w_{_H5<%Xp!%XsGL zkRzTB`IzaDFYdY_W|7`!BPiAdzH84qj27!7c z?%fOd8Acs65veGi4jIOADW*e?<|6mcXngU1mFxeV?35{}$-#FSl1IgVve|3 zS*Oy{ijMN&T*C6Qn+xn1Du7owYIenv?kHyZ!yVAz7sj$BH=51PT$r<=VnEm}KRX02 z82beqO+y8??fS;mpjjR7$!9RW+`*jGb}yqreaP5NmmNy_k&Pkh-Pc`s)r5iZUI6Vb zj3MW+nS``8l#P(HtUNhB^wdc5X;V6>gqF27oh+9bqX~Av*bi6%7;Pc~D2J$dH<` z8>}tIDnASWd;bq5T{lhtya@dPH_2gZiSZo}ub@i=bT zAG4}3hB?cT+plX9egN~AAqURlI`#*+k`VxQU(5=`!mug^l)}WX8cr5O$cdC7+}_hu z_IZwghBzWp_{L6iQzJxSgI$R5QIsHq!dO#+#r~z^=@O*2ShIh7cKOk<|9LGj6(1S< zV>s6AYc26QM*Sz*UnJLYT7Sst%buOP?>3YmvCp)`sON>O%W3lxB(f~OV|13n_p`&d z__t)X=fFXV)AHtmmKbAJAQqh7A4=u$WcpZ4k+0Q*Sl>U7QD3lED4#>Y6KoS%R`~s*x?L7S2}ps&S6<*(G*P<( z&GDWay8zi)^h62mK@G~0D4dlli@{)Dh*85XYVb;e`b^n2s`9wg-RN5*&GEN&C7_?)8-Ml!h2(ii!q@%ES-4lq{ z@k1-eV$|~GAyx~LM4k#G!y|i4UlGh+5*9bFg{eki@SgE~MtE8K@E{_WQNO4fn!q zctZU;V*J`|%(cWB!~Hy1W0@~&i3dI{F-$_F$BrV8Ki3l1=33%$lj{UmOKfSNsdV$6 zWMRihrX@ZGEpd`*i49jv+}d=K=2S~8__V}@oZLzWQ%gKnQA@1;2`#bV(-JpG^$}=^ zkH1b!jIm0X$P1>Hm|JU!@6-}!@75A$9+#H5i(2A1Xo(wsEpewXir=9n9xHM$fLh|H zUrRjcEozA;pKFPanU=V_s3ksyx%#=5c<`;Y#9U%}#Q(CEc&h+fVbBhf!$VI?oS$on zsp4sg_sLd4^l6DV4-eS6ieF1y_?Dd;{&0H4S1wk$X8(8C|HuFS=Ewh9v#;5|FZ;Ky zm&?ZfEw#kU#{Mm1)Jw;nPp&NcLt5f_9ExMzw8X8?1JAywC9X3qvCf8|khgals(gCk zjo1iH!(jDsuw&e2mbdVhmN?t5PJ420Y7bxR@ovfrg8h9NtF*u^3x?kv}7 z+{%{i9Z%}PXGa0Gt0i{zw6PO~PCozFH=!W@xv39kG(&FT{ z8YuR=w8Y04eQ9F43#&&R8w7Oyfr`;7mY}r;ztpTw7xIc;$HpB)a0VzXh9I3~8Ns#t zTuV%1W5`hn8ewUoKCMS!x&qiYa7QW3;oGf&UO4N036AJVi#zoImH9ig#7)|%ptca^ zA@-iVI<(>r4qrD*Gj;(5#gM5QvRrH!s2hg42~S$wQG&&OkCqtTIsO0|;c}8*cY_K&VrVhnicfm z#TK)Bt=vI&l5r{~2#Py(mODB-giR7qIZov3!*RJCEcQh$abv0_Zo!*JAQDE6Fr)~U zVK3NX;{=n+gU*;G4=*YBzClno90DD&t)?Mau^uG$hqT0!f9N9m-}QIxyg#5{9_1`E z`>Ks$*X*muzK&6^*;k9*i^x0zE%9Gwf4REs4`_+0GX3ekiW5!|9~Jw8mKan9;p9HZ zgAtZDFe^fXgbcQ)2J8V5YqUiUwu(z(GNdp*5fGh33JPV5LrJ*6o>62U-(G%p&=MDK zX^HKrmY6q$PZixDyPqn!g$&5)gnC-#Kk40S1r~yuxQR?g4NV4J-d+1!V;U4;JXSMG z0N5YU64TVj?F)DS)T-$JRVgdGIZ>#eF7 zKz5RuYKcJ%euib9937@Uf{rX^L`vg9f7>)CW6P|=%4o(;YmUNC2Iw4R*jx|6;omn^ zR{mA?&*HT7XsRVPFRwm16@wg`7VLkiW^zaN=rBjarCv2JiBoovAU63?qK5N1o0!I$ zu^$D2{SGa$>1v7jo*@`*n4L{)rPrDk@0dBR7yFR1u3@)Iv*EI1HD|7k1+P($i}igN z4EFoA#C*XJE;7|-r~=!*Rw@Tp3)*yRf5*9Mq|Aw06)}#D`#y0aWM_*7p0cz!LNM6r z16pF}+g!v(Vzi=9CTB3uC_!S2?#^bf*wr;1d>!rNq9c-w$ADv~Kf9z6gE?6}(4G5Aqz4GWEgGaNNN*y&OK$a7>oj|-vcwlu_Iy6 zz#|HPZE1~Zi=iDQE{n`c@p+Ztn2PWypuFlj5?$^eaSO(W2+Sco9m z9Y>y?jd@zVI$5Katr1T#$s^5TK5$x>tpo?zSoaHaa#)6h@Nir+pO=AsKmu4!rdCNz zTcba<71y?7hjVPs9`mcwT8KwrQTMqBCRRB6&m$6<Au5%{!W<7D}^Of#jX z_HS}G`3pH!1| zmxkq0nw+D4re%_?E#8CMDEmm6bsGj3oZ8Ia$g$AOsa0s1q`|PbKnX9AAk*Hjymfn{ z_K2)9oV8+yGpwEXq9|ar6>;r~_(V?#Pse2#%&@iVyj3l0e06wa%NQ^{>j4){sX-}C zj$XJOMs)0+myJCq$`ZO0t!??$53~<%tNrZndvR*8tVHEgqb3}CecC24_E50KiEJ-t zY3R{TBSaTNiP+;QUeb74aaO$o8zXk|>}hTjiX=XGT-lGCYfd*(@Uqq%O6h>Swdt)R zKyE8-shSPg^YKgxCZ(&_4xkD6BpD?j3eQKFc*nb?u7QVUzV?h!p+g0`MKJV&K6B=DW9R$WmSi)6+-Ru4>aQ1R1|F) zO0C;(pF9vP$|%F&QI5?DwSrF`XaLhuqB&UzO>N$lDW|4^MdSESBj##ug^DC`6fbQ3 zA){hs$&xwO5y058w|R>R5i-;;5s7w>OIH&23Avt#m$JSk!s}|S;8^8|Eat%Ko ze&O4~fWbZ-6!CyN&Y44Qf6y2zw~y|EE*%N76?(C_Go>^9SOf?Z3QbxXODWY#eI1 z;~r}_>N%PPPeRW<1jeEap9$uG4LtubT95p32YPPed&9=qV0EInZL0F?EH)UU7~JuJ zLsdf4ZAiviGb95!V2ra~=ARl7XFQjz2R*Nr_^`psc|YY&!?g}WM%NCr3;^t*F*gc* zy+J4u%@Vz>KKkJ+prcxLGu7~T)7r9A-JkOK^0je0FU`5`iYFO=!k*QFO13!q9a{O_ zZw2C{<;#7@<5)+WAm5DLC36&yl0t0;w{g%A+YeYm{S(|$jy{eDKUYoO^lg2Ks#y_L zB1;>`NPau@{OR0rC877j<{9ScxA@=S_`Z&o5&j}?RE~rY`Iw2fIh9iye$Gg7E4?Kj zPtH*`qW0g?SfvzC!#=KW&<%iOzP&^4!9}&;aMRAp(LQoUpMUpJ0I98eMu>0TQRnp{ zg5n>WK(Equ3V7;uXJ%Ba4ucP#_S>qtr{~yaMtRwBJNZdEyJ?iZk{8D_Wp0)N`-f?U zYRmU{c;OR3eju~-5|^1U^a4_*ma6BDVuIT#6!+nkNV)|1h7(MaA7 zY?RMF*dcRV=Fz&XmKi?qs_sH7N{L&k=ha1j4gfiRux9qGh7OwWv^uP6_%Tkvs);6k zHO){!vNl6VQ%;}J22<-|v>J2GJyz@5^O1$X9$<0smNso{bYD2HchtJ(GEsC{OuIWL zpPhLoT;=Euc+qkL!(T zJ2&F@mgyH3U5+_3S5R%ou%oon5^>Vy=>aWE3I2|43i$Xxo{IYFPJ`R3Ee6-spXhqE zZ$?ejCzx;EiPD@-4pmTqodjsJ%M|6F4r3jqO?)zJsmV2^Rzm{+MB^L+t(+SrzL(ee z%GKW5H|3bReLZWB4AIGX(6MKe)ex^qDVC{OXybEzc3De=-}Uj|R6F#wWf)BcP9a$` zCP%7vvcBZ(fqjg6$e4Uy?=mMygbKW7x9b29&xZydX;9byaf#^h=~Fr8!IY8()NtE* zSP2gL##Riz8d0-@yCen%V~{}APRE&0eyF8Fjqqtd?ZKrI43|al&h$>@4$CF_eTME+ z$duU%u_J6D3r_V8$2c%NoTjikD5Yz>&;_hGJ~rK{36ZjOdi^Su%T0W@q1IrSw|EJM zNj!geVh_W$pm{$|6S>lZKvBO1acBpMAh6xMN@z0w6TIf!{Z4gn$h|}!R%Tx~VmFc? za3*>m?8gV_Z9RK`MDqli$cX}Pz(goW^5`{)2 zlmRGK7_^yTklQe*P5+7$juj_)Ut%2Got_HD(|*?RiK{2>xCyyU_-&kYWRLHk>mDCzzU?$S zKWs@NBcBbh5cx=h2*h_3uS-#)n8L{(nig=P$;gf$jzsZ_aSiVW_7MF=V>LXspZPr6 zX!-Qu#?Nokvl$U^dyi^pz8Mbku=bVr+UoN5DZtyIbuF0)ibJOvih#Gk9C>Zw-9(-4 z)nUI3U9R~BO4dcqaIRSIzpDBW)a|xAH|~GbS`ke|G^S}+Um2Md>kxApE>qXYM)5*- zZKfq-@H`J4ddmnS@9cJb#flK)pY9|nGs&--0o`ZVb1!D(5(MtW8LF#wSDVov<9svz zd})E(^;;<>eSgw=F```GDUhr3{Llp+$#3Q^>NnI68)pi&56)_%V`=R(m@cHtS9WQ!%8gfzQPZK$Onuy(M!UY;1OJ%!*qu;2|31K9wexfs*g z=JrCVFR*E-2YiGEg6h4uT;P;@2)hh%rc(JaqJIdQcX zd(euJw6}S_-K`O?>^y0!p^V0!@76Jlv%@LcXpCF0SqCU_*QE#}i2e{GeJf$L(H%dl z15A#=iP%bT@HUFq-Ifugt&CcH%e8iaeIokJ>Xa$^P;Bgz_je_XI9BY;zc#vmqS1VS znkGw6WD2RH=H@M`g}3NatOOj{$x<0x7gLVGIRVER7c6aq8)z{>3ok%(3!;YwhFV6b zI$z5(DU!2P*t!9l;bI$J=%16^>c6@ZDukGNC~uA%$O1$7oXN9isS5Lxik}yhNl9?* zi@6(Ld95+vai{=y(`C=CWNDL7=_OWK^O^mY1hd`%sQE-wQr5X(Bc|}%%rnrhLs@eU z1yE3|qX_C0f=cNLapFB?4Eq>S39b<7`4@~I3(LM5_Uxuek&+um(BzG5&B(Ofonb1! z5S>4i$Tugo@X!!VF>2yB9ud$POP+YX(@L|3>O)+i z<{bVY-2}*0>)(3_ngQ=v{F8F6zO3*^Jh>SIl^QB(^hn)HIp1OrM#(3dZ!4{U+9pB< zfX#>gZ;G~l)bc1PH9c|WR5NYqezW``V)*Q`v^Let>NwG#vVUwR{{Se?Y=Dby*LW9A zXA&;;4vv`8j=(7tapu>-+oOV?SfwY4{|jjyfyF?`3m7$f&0-}vZ*ZB!qPx$tfF9I6iY*baXnC7k2f zK1v&}s1&l3wv=7!q%-oFmV-<1jOJBd6H%mzDYjVoV#F{Hwpn;pV*fyDLuHRn#=UOLDg1|p9G zTUSO6$=GyV=S4YeV6Urr+RUsalMFB1VvH_+rb%aJ=rz-3-jr$UjJp3?D10|H+Q_m% zuClsrM^+|#p>{sK$4B$&dOzv#YE-=DPk+_I+$khw-rhFmi9&OrR)3SVsegyA6T-Q5 zpki~5_d2E8Hnqo9&zqTX_nBsQ-Yd|UQS~=z>tw+<#Fvh-A32gzi0BMIzoE$ zHBxio>$MkpC#8Tb=|HtAaqGjM@(z2hHK;qsDw5)Ktw-00^o;P!R}{(n&a1zcLOUYD z&zU5=t}|fPiRPKxb)tY)r?5bYLo zgEKAC4mEh6d7s|53t#2p3-FEOdLDzcTKlHJzYNpVm1Ui)7W7W)X*JS13npJ{k6MmA zW{7BoQRd4oc6GfkdgJxI=Yu$^l$%Dwy7YWZ7RcpDX-U++mXjI?DleoP+A%O& zpJ^^_j^AlYS2liJxPX^a?GE);`#Lz|kP~;;%p4i!u!+00(-z|vZWs62NIq5;0S_`5-IpJOC8U+GL~yiqi5R3I5Gx%ZV>6@lIJx=}aQpDVW>0USGN*{5_L z3yoYHzus&4x7uT_ZZFm06CF^fw3MR-cGi8zLkg7z%a$^pMwVnq!<*T!=do>*!2HsS z!TSfkP2IDLf8VAQlKT)BE7C^a&Y7(kmULFR*~RINijy`Kfy@NA>YEX% zmgQHs6`|s4j_VtHa0mbjQA$mZ>F{LsC^%o^sw%>C3Ji+B9#@<=*hAB8){?U`jGn9R ziCJeL02xpsUqd(EA}?t;_C<+S?4H-6u>_Wz$H$QnFw&@^^s=egTXE7oGaQm2Ner_t zDJ`gyGYbL7=WVw6|NevMj~rW0bVNR6++~wh?aWoS57f1{d0>+#-428;e7y`b%YW>B zOy_)mqP@S#S;>LXv?l3^GidH17qkM?2INTp=o29-?uu46%j?>WkH!Q&8O7NXb{cIQ z>92?u&x-?2?fD$Bd;rJ|0J}|SJkD_bC7ha`UFeikJ3EnrtrFI31vUyzyGk)|~3C6fHY6`+}t!v%o7?vZ~gSK=JfrI4fJ}KkJ zFHqrYg=LTmf=<_E#1x_8H;%`?D-zaB?JrLkup->^7IS12C#7-QruU(gbj_m0@k0j* zS3?CZ3l87BQRX{=YNGM9b+2w@M3l$MFfVs;muRVt&nL}KG3Dsqcsbp}HRaU!GBqP7ZMb(@#x2 zp3rIo$A2*nh^a7Yt!?5s0q>eQCGMo(cMAjQj<`{cA12%;^riliYWvc)ZX>=s;@|k9 zJnZYv-^^x88*wo96RPxDb_m4>Wl!rG$uXA~U+w0rndU1-46BOmqE%N6(%fStem=k# z)I7#XIpSpZGb$p%?SVd1!GE&DECBubgA_=7M|^9g*~v{Oh>8fhYZjc>(4j) zPsjv-c9p8+dz6@_$Q0uYzdT42xZXiTR-w9^Hy$3_{AO)!86IA|B|WEzzxmjja`c}Y z&EYj6KCwI4t*!qp?f>hsLK!8*ME)7kZBa%ONoI7Ll4ooB7XGvB3P`?=me)!b!=X%Wox1qa`s z15MIW9}sQXvxfrcc`544Mdjkfxo}Y@3itYmW%hAM5HFIZh5$ijYOFpM0;sRF5ofx` zMD8W-XaZYXYza#hM{(z7^}Ot&%2QcHQy%IG+A(>oeM5*j-<331YX9BiMC4{UVbZHU zs%z_dl3`=~gr%%71x&QfDsW%DHDW<}*{n#N_Ia9t8%E3TpZ00ESXf!LaljwL%#A+* zIPU9J@xjGzSxc*>@}>6<0*KY&#p*t6cC}_d*z4c+UwlT zGZiIkZ}gqxp!om~xkvv62Zpq4NT8*DCJogT3p*UcEHtB_`R z+_2=2<^7 zOx%+dH2p{HqIFOk{FsG}zci`sROMbkun5nmWWwE}hciR!VZ8FrMxgpetSu z=Zo--1gDR!J4)g`+65x-DzCp(U>-J@nE1?)m23$Bcv!K!We}03*1wbO5o5`~oLnvB z-+n&H|13BZ?d!hNe~+jY?LjEqzcZv)fUj+6hk){g2ga@plAuk znUj;49Av~-kf!0`j@?qZAVsf5u>yX3v}Amcy_S(GMmhpagO<2h@x`Y;OLH&jOBlXz zvHn~~pF1d%M}X;tFpCS(<1W(f4;f-w51M#P)aA=T7Y>I(nj2j72$Z5V-%*-4kv=!! z5oY3*-d*077$&V^?Pl{=VP3q!BZiYI+V-ZYBW3IHZEP3ixq+ppx77^--Y^s_Lb(zgP1uKhr?L7yGWtHu1^lwu$3+Qc!go-uF9y zj63my{J>VN&wzMCi zP)><6=blp+n)(kz9S~CDSdMvTpyp2d(Z7ZMKQu?CY14$e_P;F?X~ifHka79dKB0b$ zeFp+QhyT48#tq~O{tI({+$LTJ1vg)wtl>O0Z@MNNt9m@KJ?l!nO<0NAFAWC8x+0G6 z6it&M-K%{t11p|4)VAWwBc~G%JO>{UN`77q`^O_lbe`e7Mb6+I)r{W^%=7YlduY?) zAudIpIQAP7Sbq_M(Z%_IFQSG%C`FOS$2G5^!> z_u6JiTl)v2ewk>FI`^9>Kz!rZf)e>ZenBMHyE1~l{@+i^vtF8xh0L-2T87k9O75Ua zQe|439tJk>K z$8|{d2t$}&ISBx(^rwr0ZHtXGOPeoUb`l!LRA+VWC>X>!rZnZ(t4;ZaQVNsKAa~iz zF(My1;YOncgI?`jSDrdnYIm?6j3~EYbg$Hh6MSsVaqyi?xF|G z%Vu5c-B=rV4qDsFyRWHHWtc&C8BNO=DD{pM+J=~kH7?PwY3xp)ict2&z$=ptm4C&! z=U9nOi?%sOehu27<~3))P5a&FXCufCjOg5`ipb0c2;upO`nCwZ&$F(9*}XAhs2i;V zaxU*@2Ok~*f`R#zR{`1%ZQ)LyJ;=EtDImJ+RLy2u1rO`dRY$>ByZnHIsCBLnMOZCE zBl2eNGmX-1R%6bEs@jN8xgJ~Th?EM(tf|=$6r~?ChUI!yY~>8oQ~G?mwsKW6N<<4! z#1d&5efde&hOxWs2hs|)&qXHnS{fvV&O{&wjJdze#1iAG;ZwcsURh%+?W1c0@4+_5 zpA)WW%YCwuV{L{)lWDH9S4WdBFZyW50$#RdH=Xv~HxSlV`LRY9EL3zAajHg0M?qX1 z#fl-;7k#mh;(wqKa%|Kl!SV9aMCFFu-`$$)dSU65yylCD?JZaZzY$?cj znH6P3Q%lZ0akuP!w0=;6)$Byj;deKiVh26Tf19i%>UYbOnRnORox3sJ;iPkJE&!Y> z9tr8aI2|?|Ig|!=>vc^J(I&q(3W)dI4c5)<4L_)4nHC}Vp;Mzb27*wh|y8hVtjkZx@ z<%0pQI+2ZL$@TIam3uyC<2MLT9>m#NiO{821fiBxzXzDhgj1)C7UR6hNLhx?Ns=1( zS8j4UiZJxt*S9%7L9@c_x^FAtqwYEU~Oxf$4>d+W`bTRpp&`ugcFX-ozw%ycL zo4@zRBc_trman zZOMp_>l@BpPn-lDzxh=kGM%fs9WW^Q6VTc;DtqNs{vb`7xw_Tpvt(pNx1NH^0SZct zITKrV!Lj;%Rr`29*RVyBUSg{|7yYe7{D~{Nnw6uqyZK%VBZ_uWEx!45DLG|>ZoPd3 z7W<0!FOw6P;aZhJRqf8k0+wvzRN`|Bxzf< z$?4|KE*3y_<{Nj=KR07;Qf>Y?knB9yjMNRvf`a8m*UgIY?Q-#*I563_NvAUxKWck* zQ1!lBTIh+Swnj3FP#|P1)#&2UZxKYSw@=cpZ<%V(yhgWpGQY&{Q}E!rqM@)hH+gh0 z{{-~g38?w*bkcfLE2sIhVvj^zOVB>!l^d@qm}7_!0Ja`p8+M?hAkcX zE&Pbs_7Od~+UC_z2Yobab?M2GZM(8pNs2Z7rYik(+OM}SQ*r!Y>X!omSNDikEl0n5 zF4`C?hS$~eB>YLu71~+wvinBMT*<4+3|EM(2+GNX`^Gt<*S`HWRnTQuLU+h68j!E& zU)Xif^f6Y@i>#|W!FY~WZ$d^sIF8G z#o8iF30>ARc`Xgu=nhmkA@3HEAnA-tUm$L*@=`vQO~cUP8H?j1)#1^x~uxy z;Z4_W#6YpukXhN6YzHYFpZZvNszU{vkH)V=$XMe%rc=n!Gz_eG#HcPGuLCo6BfK6iG!qN4*&FxDzPKWw-jY?t zANYmdz2>9kK;Sva>Xk4Faup- zZPgzdbrj1a3v6a@nW21ILiEay$xBN0&*g?!tbtr`;>Yx<+xM&K`L?NpYsy<9hCm96 zr#9&TM!IgMpaVrNRaYsTj+m!4e!Ak)2yLVjMnjeQq9Ns_7KF=05Bq*(HTOx``}bIc z@z>}+Wy~q8Nr4E{lrLD2FTo!R2p!GxuWwD4y!HWa|mkEAM=-p zbiH6A-wl$Y(^&Cm}Pagzc7B#Hv2 zk;ffdAkXffnVI%&*OW@z$O7$caFg@>hqC2~)CjQVw3{Q|k#Q$XuApG$M1cjPaU)|= z9DingeiD)e@6Kz=c`8`r=#?fL(-PWlQbhm6cUt^wTS1F`w3LD__u*Hd3ok*0*nbp| z*?7TOB2|SxgCaO7ryTcWT+|PVOJj1a&|ZSwe?ldQ^bHB)sk6t25A#>bm@`JAZaAwx z;w3jJ;7eHyduPKqONK|jxr4^|sGzdNElO=)RFkg?H+pymVqHiSOA7E8f?lfXuU90# zx&Gw@99!;BZ`qS>)Ejl9G3|wVOF|tV&%pUAQP$am!+`fv9A-lBW_s6JQ)oYy6MK!S;&{&WA!@W}!s7tA3=N n{LQET>%qS&)4zt-ft`vtifM~-7W4}w7)(v&_Vt2mX3zfvojI}l literal 0 HcmV?d00001 diff --git a/figures/ft600_example_sch.png b/figures/ft600_example_sch.png new file mode 100644 index 0000000000000000000000000000000000000000..9d4656fb5bb08b1f223e245752e14570ee273d3c GIT binary patch literal 37378 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 literal 0 HcmV?d00001 diff --git a/figures/ft600_ready.png b/figures/ft600_ready.png new file mode 100644 index 0000000000000000000000000000000000000000..2db1aa318117e0c571a3545317b8c7fbbab6fd08 GIT binary patch literal 9363 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 N002ovPDHLkV1h-o+ywvt literal 0 HcmV?d00001 diff --git a/figures/ports.png b/figures/ports.png new file mode 100644 index 0000000000000000000000000000000000000000..b9b1eb636129f5949eaf7eea9d61ba7087543009 GIT binary patch literal 32946 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 literal 0 HcmV?d00001 diff --git a/figures/structure.png b/figures/structure.png new file mode 100644 index 0000000000000000000000000000000000000000..1e330c40d9fa754cf39b1337011a9ac75220481c GIT binary patch literal 31952 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 literal 0 HcmV?d00001 diff --git a/python/USB_FTX232H_FT60X.py b/python/USB_FTX232H_FT60X.py new file mode 100644 index 0000000..b4f7cc4 --- /dev/null +++ b/python/USB_FTX232H_FT60X.py @@ -0,0 +1,283 @@ +#-*- coding:utf-8 -*- +# Python3 + + +import sys + + + + +def open_ft_usb_device(device_type, device_name): + ''' + open_ft_usb_device(str device_type, str device_name) -> USB-device object + function: + open an FTDI USB device + parameters : + device_type : + 'FTX232H' : FT232H or FT2232H + 'FT60X' : FT600 or FT601 + device_name : the USB device's product name to be opened + for 'FTX232H', the chip's default device_name = 'USB <-> Serial Converter' unless the user has modified it. + for 'FT60X' , the chip's default device_name = 'FTDI SuperSpeed-FIFO Bridge' unless the user has modified it. + return: + a USB-device object + ''' + + b_device_name = bytes(device_name, encoding="ASCII") + + if device_type == 'FT60X' : # search the FT60X device ################################################################################################ + + try: + import ftd3xx # import # + except: + return None, 'Failed to import ftd3xx' + + for device_id in range(16): + + if sys.platform == 'win32': + usb = ftd3xx.create(device_id, ftd3xx._ftd3xx_win32.FT_OPEN_BY_INDEX) + elif sys.platform == 'linux2': + usb = ftd3xx.create(device_id, ftd3xx._ftd3xx_linux.FT_OPEN_BY_INDEX) + + if usb is None: + continue + + if sys.platform == 'win32' and usb.getDriverVersion() < 0x01020006: + usb.close() + return None, 'Old D3XX driver version. Please update driver!' + + if usb.getDeviceInfo()['Description'] != b_device_name: + usb.close() + continue + + numChannel = [4, 2, 1, 0, 0][usb.getChipConfiguration().ChannelConfig] + if numChannel != 1: + usb.close() + return None, 'This %s is not in sync-245-fifo mode because number of channel is not 1 ! (numChannel=%d)' % (device_type, numChannel) + + return usb, 'Successfully opened %s USB device: %s' % (device_type, device_name) + + else: # search the FTX232H device ############################################################################################## + + try: + import ftd2xx # import # + except: + return None, 'Failed to import ftd2xx' + + for i in range(16): + + try: + usb = ftd2xx.open(i) + except: + continue + + if usb.description != b_device_name: + usb.close() + continue + + usb.setBitMode(0xff, 0x40) + + return usb, 'Successfully opened %s USB device: %s' % (device_type, device_name) + + return None, 'Could not open %s USB device: %s' % (device_type, device_name) + + + + + +class USB_FTX232H_FT60X_sync245mode(): + + def __init__(self, device_to_open_list = (('FTX232H', 'USB <-> Serial Converter' ), + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) ): + ''' + __init__(str device_type, str device_name) -> USB_FTX232H_FT60X_sync245mode object + function: + open FTDI USB device and return a USB_FTX232H_FT60X_sync245mode object + ''' + + for device_type, device_name in device_to_open_list: + + usb, message = open_ft_usb_device(device_type, device_name) + print(message) + + if not usb is None: + + self.device_type = device_type + self.device_name = device_name + self._usb = usb + self._recv_timeout = 2000 + self._send_timeout = 2000 + + self.set_recv_timeout(self._recv_timeout) + self.set_send_timeout(self._send_timeout) + + if device_type == 'FT60X' : + if usb.getDeviceDescriptor().bcdUSB < 0x300: + print('Warning: Device is NOT connected using USB3.0 cable or port!') + self._chunk = 65536 * 16 + else: + self._chunk = 65536 + usb.setUSBParameters(self._chunk*4, self._chunk*4) + + break + + else: + raise Exception('Could not open USB device') + + + + + def close(self): + ''' + close() + function: + close the FTDI USB device + ''' + + self._usb.close() + self._usb = None + + + + + def set_recv_timeout(self, timeout): + ''' + set_recv_timeout(int timeout) + function: + set recv timeout + parameter: + int timeout : unit: ms + ''' + + self._recv_timeout = timeout + if self.device_type == 'FT60X' : + self._usb.setPipeTimeout(0x82, self._recv_timeout) + else: + self._usb.setTimeouts(self._recv_timeout, self._send_timeout) + + + + + def set_send_timeout(self, timeout): + ''' + set_send_timeout(int timeout) + function: + set send timeout + parameter: + int timeout : unit: ms + ''' + + self._send_timeout = timeout + if self.device_type == 'FT60X' : + self._usb.setPipeTimeout(0x02, self._send_timeout) + else: + self._usb.setTimeouts(self._recv_timeout, self._send_timeout) + + + + + def send(self, data): + ''' + send(bytes data) -> int actual_send_len + function: + send data + parameter: + bytes data : data to send + return: + int actual_send_len : actual sent byte count + if the device cannot accept so many data until timeout, actual_send_len < len(data) + ''' + + txlen = 0 + + for si in range(0, len(data), self._chunk): + ei = si + self._chunk + ei = min(ei, len(data)) + + chunk = data[si:ei] + + if self.device_type == 'FT60X' : + txlen_once = self._usb.writePipe(0x02, chunk, len(chunk)) + else: + txlen_once = self._usb.write(chunk) + + txlen += txlen_once + + if txlen_once < len(chunk): + break + + return txlen + + + + + def recv(self, recv_len): + ''' + recv(int recv_len) -> bytes data + function: + receive data + parameter: + int recv_len : data length to be received + return: + bytes data : received data bytes + if the device cannot send so many data until timeout, len(data) < recv_len + ''' + + data = b'' + + if self.device_type == 'FT60X' : + chunk = bytes(self._chunk) + + zero_count = 0 + si = 0 + + while si < recv_len : + ei = si + self._chunk + ei = min(ei, recv_len) + + rxlen_once = self._usb.readPipe(0x82, chunk, ei-si) # try to read (ei-si) bytes + + si += rxlen_once + + if rxlen_once > 0 : + zero_count = 0 + data += chunk[:rxlen_once] + else : # no any byte received + zero_count += 1 + if zero_count >= 2 : + break + + else: + for si in range(0, recv_len, self._chunk): + ei = si + self._chunk + ei = min(ei, recv_len) + + chunk_len = ei - si + + chunk = self._usb.read(chunk_len) + + data += chunk + + if len(chunk) < chunk_len: + break + + return data + + + + + + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + print('device opened: device_type=%s, device_name=%s' % (usb.device_type, usb.device_name) ) + + usb.close() + + diff --git a/python/usb_loopback_mass.py b/python/usb_loopback_mass.py new file mode 100644 index 0000000..6043243 --- /dev/null +++ b/python/usb_loopback_mass.py @@ -0,0 +1,57 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends random data blocks to FTDI chip. The FPGA immediately returns these data (loopback). +# The program will receive these data blocks and compare them with the previously sent data blocks (they should be the same) +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_loopback.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_loopback.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + +from random import randint + + + +def rand_data (minlen, maxlen) : + minlen = max(1, minlen) + maxlen = max(minlen, maxlen) + length = randint(minlen, maxlen) + array = [randint(0, 255) for i in range(length)] + return bytes(array) + + + +TEST_COUNT = 2000 + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode( device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + total_len = 0 + + for i in range(TEST_COUNT) : + txdata = rand_data(1, 2000) # randomly generate data (with type of 'bytes') + usb.send(txdata) # send data + rxdata = usb.recv( len(txdata) ) # receive data, it will get the same data since the usb is loopback in FPGA (see fpga_top_ft232h_loopback.v or fpga_top_ft600_loopback.v) + + total_len += len(txdata) + + if i % 100 == 0 : + print('[%d/%d] total_len=%d' % (i+1, TEST_COUNT, total_len) ) + + if txdata != rxdata : # check if send data and receive data are the same + print('*** txdata and rxdata mismatch' ) + print('txdata =', txdata) + print('rxdata =', rxdata) + break + + usb.close() diff --git a/python/usb_loopback_simple.py b/python/usb_loopback_simple.py new file mode 100644 index 0000000..7a35569 --- /dev/null +++ b/python/usb_loopback_simple.py @@ -0,0 +1,33 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends 16 bytes to FTDI chip. The FPGA immediately returns these data (loopback). +# The program will receive these bytes (they should be as same as the sended 16 bytes) +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_loopback.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_loopback.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + txlen = usb.send(b'0123456789abcdef') + + print("%d B sent" % txlen) + + data = usb.recv(txlen*2) + + print("recv %d B : %s" % (len(data), str(data)) ) + + usb.close() diff --git a/python/usb_rx_mass.py b/python/usb_rx_mass.py new file mode 100644 index 0000000..628fdab --- /dev/null +++ b/python/usb_rx_mass.py @@ -0,0 +1,55 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends 4 bytes to FTDI chip, and the FPGA will treat these 4 bytes as a length. +# Then the FPGA sends bytes of length to the computer, and the program should receive these bytes. +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_tx_mass.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_rx_mass.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + +from random import randint +import time + + + +TEST_COUNT = 50 + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + total_rx_len = 0 + + time_start = time.time() + + for i in range (TEST_COUNT) : + expect_len = randint(1, 10000000) # random a length + txdata = bytes( [ expect_len&0xff, (expect_len>>8)&0xff, (expect_len>>16)&0xff, (expect_len>>24)&0xff ] ) # convert length number to a 4-byte byte array (with type of 'bytes') + + usb.send(txdata) # send the 4 bytes to usb + + data = usb.recv(expect_len) # recv from usb + + rx_len = len(data) + + total_rx_len += rx_len + time_total = time.time() - time_start + data_rate = total_rx_len / (time_total + 0.001) / 1e3 + + print('[%d/%d] rx_len=%d total_rx_len=%d data_rate=%.0f kB/s' % (i+1, TEST_COUNT, rx_len, total_rx_len, data_rate) ) + + if expect_len != rx_len : + print('*** expect_len (%d) and rx_len (%d) mismatch' % (expect_len, rx_len) ) + break + + usb.close() diff --git a/python/usb_tx_crc.py b/python/usb_tx_crc.py new file mode 100644 index 0000000..3205357 --- /dev/null +++ b/python/usb_tx_crc.py @@ -0,0 +1,88 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends random data block to FTDI chip. The blocks all ends with 0xFF. Except for the last byte, all other bytes are not 0xFF +# when the FPGA receives the block, it calculates its CRC. When meeting 0xFF, the FPGA send CRC of the block via FTDI chip +# Finally, the program will verify whether the CRC value is the same as the calculated CRC value. +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_rx_crc.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_rx_crc.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + +from random import randint +import time + + + +def rand_data (minlen, maxlen) : + minlen = max(1, minlen) + maxlen = max(minlen, maxlen) + length = randint(minlen, maxlen) + array = [randint(0, 0xFE) for i in range(length)] + return bytes(array) + + + +def calc_crc (data) : + TABLE_CRC = (0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c) + crc = 0xFFFFFFFF + for byte in data : + crc ^= byte + crc = TABLE_CRC[crc & 0xF] ^ (crc >> 4) + crc = TABLE_CRC[crc & 0xF] ^ (crc >> 4) + return crc + + + + +data_list = [ # this list will contains several data blocks and their CRC + [ data, calc_crc(data) ] + for data in [ # Note that each block of data end with 0xFF, since FPGA start to send CRC when meeting 0xFF (see rx_calc_crc.v) + b'\xFF' , # block length = 1 + rand_data( 1 , 1 ) + b'\xFF' , # block length = 2 + rand_data( 2 , 2 ) + b'\xFF' , # block length = 3 + rand_data(2000000, 3000000) + b'\xFF' , # block length = 2000001 ~ 3000001 + rand_data(2000000, 3000000) + b'\xFF' , # block length = 2000001 ~ 3000001 + rand_data(2000000, 3000000) + b'\xFF' # block length = 2000001 ~ 3000001 + ] +] + + + +TEST_COUNT = 50 + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + total_tx_len = 0 + + time_start = time.time() + + for i in range (TEST_COUNT) : + txdata, tx_crc = data_list[ randint(0, len(data_list)-1) ] # randomly select one data block, and get its CRC + + usb.send(txdata) + + rxdata = usb.recv(4) # recv 4 bytes + rx_crc = (rxdata[3]<<24) + (rxdata[2]<<16) + (rxdata[1]<<8) + (rxdata[0]<<0) # regard the 4 bytes as CRC + + total_tx_len += len(txdata) + time_total = time.time() - time_start + data_rate = total_tx_len / (time_total + 0.001) / 1e3 + + print('[%d/%d] tx_len=%d crc=%08x total_tx_len=%d data_rate=%.0f kB/s' % (i+1, TEST_COUNT, len(txdata), rx_crc, total_tx_len, data_rate) ) + + if tx_crc != rx_crc : + print('*** tx_crc (%08x) and rx_crc (%08x) mismatch' % (tx_crc, rx_crc) ) + break + + usb.close()