Last active
February 27, 2025 11:08
-
-
Save hackfin/0b124e280c42f5099aa7d6464cf19ee9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"id": "56dc03bb-e075-43a7-b098-bcda6e41e6c3", | |
"metadata": {}, | |
"source": [ | |
"# JPEG encoder cosimulation\n", | |
"\n", | |
"This notebook demonstrates how a black-box cycle and bit-accurate simulation model of the `jpeg_l2` dual lane encoder is run interactively against own Python code.\n", | |
"\n", | |
"To run the notebook, you need to launch the cyrite JupyterLab environment via the button below.\n", | |
"Then drag a copy of this file into the JupyterLab file pane or use the git command below to clone a copy from the gist repository.\n", | |
"\n", | |
"[](https://mybinder.org/v2/gh/hackfin/cyrite.howto/master?urlpath=lab/tree/index.ipynb)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"id": "cffaa3c5-9c4c-425a-b5a8-438afb76034c", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Copy this line without comment into a Jupyter Notebook cell to check out this file:\n", | |
"# !git clone https://gist.github.com/0b124e280c42f5099aa7d6464cf19ee9.git jpeg_l2" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "d1654eaa-8732-4e37-9239-8a0006c6d65d", | |
"metadata": {}, | |
"source": [ | |
"This public evaluation version is restricted:\n", | |
"\n", | |
"* Only PNG images in RGB format, maximum 256x256\n", | |
"* No runtime parameter changes permitted\n", | |
"* YCbCr 4:2:2 decimation only\n", | |
"* No restart markers, no EOB optimization, fixed huffman tables (default)\n", | |
"\n", | |
"It is released for:\n", | |
"\n", | |
"* Academical purposes\n", | |
"* Demonstration of cyrite Cosimulation ecosystem running in a Binder (no software installation required)\n", | |
"* Evaluation for integration purposes\n", | |
"\n", | |
"The likewise co-verified synthesizeable code of this encoder can be supplied in VHDL (1993 standard compatible) and Verilog (2005 syntax) under individual agreements.\n", | |
"\n", | |
"## Co-Simulation details\n", | |
"\n", | |
"The hardware model which is a compiled event based model of the JPEG hardware encoder design runs as an executable DLL and is driven by the co-simulation handler from a Python main loop every clock cycle. It is thus somewhat slow, but Python can be given cycle-accurate control to the simulation. In this case, a co-routine delivers the data from the image array according to a simple video timing with pauses between lines.\n", | |
"\n", | |
"The configuration of the JPEG encoder is done prior to feeding data. Wrong settings will result in internal FIFO errors that can be monitored in particular.\n", | |
"\n", | |
"## Installation\n", | |
"\n", | |
"Uncomment the line below to install the wheel package. This may also install a specific cyhdl package or extension, so this may modify your currently running container." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"id": "4b7996a0-d343-4639-ae1d-883eb69d8571", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# pip install https://section5.ch/cyrite/jpeg_l2eval-0.1-cp310-cp310-linux_x86_64.whl" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "5350d73e-286f-42b9-8b82-d7543524b12b", | |
"metadata": {}, | |
"source": [ | |
"## JPEG hardware conversion" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "22b59d2e-ae93-430d-b057-648cb6b3e6a7", | |
"metadata": {}, | |
"source": [ | |
"You can simply drag a PNG file with maximum 256x256 size into the left pane. Or pull an image file from famous reference image collections, such as [https://www.r0k.us/graphics/kodak/kodak/](https://www.r0k.us/graphics/kodak/kodak/).\n", | |
"We do that via the PIL library:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"id": "0426e486-c8fd-4b4f-91cb-ffd336a46bc6", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from PIL import Image as Img\n", | |
"import requests\n", | |
"url = \"https://www.r0k.us/graphics/kodak/kodak/kodim01.png\"\n", | |
"im = Img.open(requests.get(url, stream=True).raw)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "eb60ff44-38bd-4d11-944d-0dbbe310762a", | |
"metadata": {}, | |
"source": [ | |
"Then we crop the image and store it:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"id": "cb5ee110-a442-4f0a-a40f-7f825b8e6a33", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"x, y, w, h = 300, 150, 256, 128\n", | |
"im1 = im.crop((x, y, x + w, y + h))\n", | |
"im1.save(\"test1.png\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "fe76181d-db3b-4c04-9f51-b83702472872", | |
"metadata": {}, | |
"source": [ | |
"Then import the co-simulation test bench:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"id": "5838d81f-22b3-44a7-9ce3-547d4b7af496", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from jpeg_l2.test import test_cosimulation" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "380cdb10-0199-449d-9659-5725bbe46d63", | |
"metadata": {}, | |
"source": [ | |
"Run the test bench with the PNG image as argument:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"id": "df656945-3abb-483e-9396-e36fc6d6200b", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG INIT <class 'yosys.simulator.CXXRTL'> False\n", | |
" api_version: use default 1 \n", | |
" ADDR_WIDTH: use default 11 \n", | |
" DEMO_VERSION: use default True \n", | |
" CHANNEL_A_BUF_DEPTH: use default 10 \n", | |
" CHANNEL_B_BUF_DEPTH: use default 10 \n" | |
] | |
}, | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"\u001b[7;34mAttempting cold import of module `.runtime.jpeg_l2_eval_rgb` in package jpeg_l2.test\u001b[0m\n" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG: Ignore signature mismatch: jpeg_l2_eval_rgb_mod_JPEGEvalu1u1u1u24u8u1_jpeg_cfg_mod_JPEGL2_CosimEval_in_QRamPort_cls_Signal_in_1_11_1_10_10 != jpeg_l2_eval_rgb_mod_JPEGEvalu1u1u1u24u8u1_jpeg_cfg_mod_JPEGEval_in_QRamPort_cls_Signal_in_1_11_1_10_10\n", | |
"Open for writing: tb_jpeg_l2.vcd\n" | |
] | |
}, | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"encode: 100%|███████████████████████| 127/127 [00:07<00:00, 17.20it/s, line=127]" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"STOP JPEG FILE OUTPUT\n", | |
"Got JPEG done signal, finishing JPEG\n", | |
"DEBUG SIMULATION DONE\n" | |
] | |
}, | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"\n", | |
"\u001b[7;34mSTOP SIMULATION @607160\u001b[0m\n" | |
] | |
} | |
], | |
"source": [ | |
"test_cosimulation.run(\"test1.png\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "f6c17bf8-c635-426f-b8ba-8dacd168ab92", | |
"metadata": {}, | |
"source": [ | |
"Once the simulation has terminated, two files are created:\n", | |
"\n", | |
"* The JPEG result `test.jpg`, composed of a software header and the stream from the hardware encoder\n", | |
"* A wave trace dump `.vcd` file that can be displayed using a local waveform viewer such as GTKWave.\n", | |
" These files can get very big, thus, resolution is limited to 256x256 with this test bench." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"id": "f21a882c-8f9f-4bf1-82ce-bd4b0edd5fd2", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"-rw-r--r-- 1 cyrite users 161020811 Feb 27 10:42 tb_jpeg_l2.vcd\n", | |
"-rw-r--r-- 1 cyrite users 12813 Feb 27 10:42 test.jpg\n" | |
] | |
} | |
], | |
"source": [ | |
"! ls -l test.jpg tb_jpeg_l2.vcd" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "832e2a8d-381e-4025-9a2b-2590d22b3fb6", | |
"metadata": {}, | |
"source": [ | |
"## Displaying the resulting image" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"id": "39e68bff-4fda-4340-ba23-52a963b2caad", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"image/jpeg": "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", | |
"text/plain": [ | |
"<IPython.core.display.Image object>" | |
] | |
}, | |
"execution_count": 8, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"from IPython.display import Image\n", | |
"Image(filename='test.jpg') " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "944a655f-adc9-4a88-af83-63b4f2dcaeb3", | |
"metadata": {}, | |
"source": [ | |
"## Modifying the test bench\n", | |
"\n", | |
"To create own test benches, you must derive a class from the `JPEGL2_CosimEval` class below. This class implements a copy of the crucial `JPEGL2_CosimEval` methods for a more verbose explanation below." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"id": "5effc5ca-657d-4be6-97c0-8c404a4304d0", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from jpeg_l2.test import test_cosimulation\n", | |
"from jpeg_l2.jpeg_eval import qval, jpeg_cfg, QRamPort" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"id": "0d0615ac-5163-47e4-aef2-cf1fb29b7873", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from cyhdl import *\n", | |
"\n", | |
"class MyJPEG(test_cosimulation.JPEGL2_CosimEval):\n", | |
" def __init__(self):\n", | |
" super().__init__()\n", | |
" self.quant = 4 # Default quantization\n", | |
" \n", | |
" def coroutine(self, dval, rgbdata):\n", | |
" \"The co-routine that is called every clock cycle in native python\"\n", | |
" if self.p > 0:\n", | |
" self.p -= 1\n", | |
" dval.next = False\n", | |
" else:\n", | |
" dval.next = True\n", | |
" a = self.img_array\n", | |
" v = [ intbv(int(a[i][self.y][self.x]))[8:] for i in range(3) ]\n", | |
" rgbdata.next = concat(*v)\n", | |
"\n", | |
" if self.x < self.width-1:\n", | |
" self.x += 1\n", | |
" else:\n", | |
" self.x = 0\n", | |
" self.p = self.hpause\n", | |
"\n", | |
" if self.y < self.height-1:\n", | |
" self.y += 1\n", | |
" self.progress_update(self.y)\n", | |
" else:\n", | |
" # print(\"DEBUG NEXT FRAME\")\n", | |
" self.p = self.vpause\n", | |
" # self.y = 0\n", | |
" \n", | |
" @cyrite_factory.testbench(\"ns\")\n", | |
" def tb_jpeg_l2(self, quant = None):\n", | |
" clk\t\t\t\t\t= self.ClkSignal()\n", | |
" reset\t\t\t\t= self.ResetSignal(False, True)\n", | |
" dval\t\t\t\t= self.Signal(bool())\n", | |
" rgbdata = self.Signal(intbv()[24:])\n", | |
" jpeg_out\t\t\t= self.Signal(intbv()[8:])\n", | |
" jpeg_valid\t\t\t= self.Signal(bool())\n", | |
" cfg = jpeg_cfg(self)\n", | |
"\n", | |
" if quant > 40:\n", | |
" raise ValueError(\"Quality too low, use between 1: best and 40: lowest\")\n", | |
"\n", | |
" if quant is None:\n", | |
" quant = self.quant\n", | |
" else:\n", | |
" self.quant = quant\n", | |
"\n", | |
" # Initialize quantization tables with inverse:\n", | |
" qt_data = [ qval(i, quant)[1] for i in self.QTABLE_LUMA_ZZ ]\n", | |
" qt_data += [ qval(i, quant)[1] for i in self.QTABLE_CHROMA_ZZ ]\n", | |
"\n", | |
" # Instance a backdoor access to the quantization memory:\n", | |
" qr = QRamPort(self.Signal)\n", | |
"\n", | |
" # Finally, instance the simulation black box that runs\n", | |
" # as executable hardware model:\n", | |
"\n", | |
" uut = self.jpeg_l2_eval_rgb(clk = clk, reset = reset,\n", | |
" din_valid = dval,\n", | |
" rgbdata = rgbdata,\n", | |
" jpeg_stream = jpeg_out, jpeg_valid = jpeg_valid,\n", | |
" cfg = cfg,\n", | |
" qram = qr,\n", | |
" )\n", | |
"\n", | |
" @self.always(delay(8))\n", | |
" def pclkgen():\n", | |
" clk.next = ~clk\n", | |
"\n", | |
" @self.always(clk.posedge)\n", | |
" def jpeg_writer():\n", | |
" if jpeg_valid and self.jpeg:\n", | |
" self.jpeg.feed(bytes([jpeg_out.evaluate()]))\n", | |
"\n", | |
" @self.always(clk.posedge)\n", | |
" def cosimulator():\n", | |
" if self.coroutine_enable:\n", | |
" self.coroutine(dval, rgbdata)\n", | |
"\n", | |
" @self.sequence\n", | |
" def main():\n", | |
" reset.next = True\n", | |
" cfg.jpeg_start.next = True # Prepare arming the JPEG encoder\n", | |
"\n", | |
" # This initializes the quantization memory.\n", | |
" # Normally, this is done by an embedded minimal CPU\n", | |
" yield from self.initialize_ram(clk, qr, qt_data)\n", | |
" yield delay(10)\n", | |
"\n", | |
" reset.next = False\n", | |
"\n", | |
" self.start(\"test1.jpg\") # Start a new JPEG frame\n", | |
" # These FIFOs need to be reset prior to arming the JPEG\n", | |
" # encoder\n", | |
" cfg.fifo_reset.next = True\n", | |
" cfg.fifo_reset.next = False\n", | |
" yield clk.posedge\n", | |
" cfg.jpeg_start.next = False\n", | |
"\n", | |
" yield from cfg.initialize(self.width, self.height)\n", | |
"\n", | |
" yield delay(40)\n", | |
"\n", | |
" yield delay(20)\n", | |
"\n", | |
" yield clk.negedge\n", | |
"\n", | |
" while cfg.jpeg_done == False:\n", | |
" yield delay(5000)\n", | |
"\n", | |
" self.stop() # Stop frame and finish image\n", | |
" print(\"Got JPEG done signal, finishing JPEG\")\n", | |
" raise StopSimulation\n", | |
"\n", | |
" return instances()\n", | |
"\n", | |
" def testbench(self, quant):\n", | |
" # Helper function to initialize the test bench\n", | |
" # and set the correct path for the runtime\n", | |
" tb = self.tb_jpeg_l2(quant)\n", | |
" tb.ignore_signature = True\n", | |
" tb.register_distpath(test_cosimulation.__name__)\n", | |
"\n", | |
" return tb" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "feba09d5-8b84-429d-bd37-e724d03cdde5", | |
"metadata": {}, | |
"source": [ | |
"The `main()` function is a purely sequential generator function, passing control to the simulator whenever a yield occurs. All other processes inside the `@cyrite_factory.testbench` run virtually in parallel within the simulation context. What happens:\n", | |
"\n", | |
"* A `load_image()` call creates an internal image array and loads its initial data from an RGB image\n", | |
"* Once the JPEG encoder process is started, the `coroutine()` will feed the virtual video interface with RGB data\n", | |
"* A `jpeg_writer` process feeds the **software** jpeg streaming layer with data. In the encoder 'system on chip', this is handled by a DMA autobuffer streaming engine.\n", | |
"* When the JPEG encoder signals a `jpeg_done` condition, the `main()` process finishes the JPEG\n", | |
"\n", | |
"This co-simulation setup can thus be enhanced to process image sequences or use different streaming methods such as MJPEG video streams." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "85915fb1-5a52-446e-9b11-a9e18d9cb003", | |
"metadata": {}, | |
"source": [ | |
"## Invoking the test bench\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"id": "092f1b9f-62a9-4bb9-9e21-dbcd3923b772", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"\u001b[7;34mAttempting cold import of module `.runtime.jpeg_l2_eval_rgb` in package jpeg_l2.test\u001b[0m\n" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG INIT <class 'yosys.simulator.CXXRTL'> False\n", | |
" api_version: use default 1 \n", | |
" ADDR_WIDTH: use default 11 \n", | |
" DEMO_VERSION: use default True \n", | |
" CHANNEL_A_BUF_DEPTH: use default 10 \n", | |
" CHANNEL_B_BUF_DEPTH: use default 10 \n", | |
"DEBUG: Ignore signature mismatch: jpeg_l2_eval_rgb_mod_JPEGEvalu1u1u1u24u8u1_jpeg_cfg_mod_MyJPEG_in_QRamPort_cls_Signal_in_1_11_1_10_10 != jpeg_l2_eval_rgb_mod_JPEGEvalu1u1u1u24u8u1_jpeg_cfg_mod_JPEGEval_in_QRamPort_cls_Signal_in_1_11_1_10_10\n", | |
"Open for writing: tb_jpeg_l2.vcd\n" | |
] | |
}, | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"encode: 100%|███████████████████████| 127/127 [00:07<00:00, 18.05it/s, line=127]" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"STOP JPEG FILE OUTPUT\n", | |
"Got JPEG done signal, finishing JPEG\n" | |
] | |
}, | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"\n", | |
"\u001b[7;34mSTOP SIMULATION @607160\u001b[0m\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"0" | |
] | |
}, | |
"execution_count": 11, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"m = MyJPEG()\n", | |
"\n", | |
"m.load_image(\"test1.png\")\n", | |
"tb = m.testbench(14)\n", | |
"tb.run(-1, wavetrace = True, recompile = False)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "fc35cff6-d1b0-48d8-a1e3-508ca75cbb9e", | |
"metadata": {}, | |
"source": [ | |
"## Image comparison\n", | |
"\n", | |
"We used different quality settings on the encoding, thus we can see differences in the JPEG outputs below:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"id": "2cb059b1-1a55-40bc-9d62-620fefebd5f0", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"-rw-r--r-- 1 cyrite users 12813 Feb 27 10:42 test.jpg\n", | |
"-rw-r--r-- 1 cyrite users 10513 Feb 27 10:42 test1.jpg\n" | |
] | |
} | |
], | |
"source": [ | |
"!ls -l *.jpg" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"id": "33e07ce1-c23a-47f0-beb5-a553cb5c46d2", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"image/jpeg": "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", | |
"text/plain": [ | |
"<IPython.core.display.Image object>" | |
] | |
}, | |
"execution_count": 13, | |
"metadata": { | |
"image/jpeg": { | |
"width": 512 | |
} | |
}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"Image(filename='test.jpg', width = 512)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"id": "dba9b32a-5abf-483a-b9da-cf22ce8182c5", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"image/jpeg": "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", | |
"text/plain": [ | |
"<IPython.core.display.Image object>" | |
] | |
}, | |
"execution_count": 14, | |
"metadata": { | |
"image/jpeg": { | |
"width": 512 | |
} | |
}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"Image(filename='test1.jpg', width = 512) " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "b3daac53-d055-4354-9e00-4ef28de50307", | |
"metadata": {}, | |
"source": [ | |
"## Wave trace\n", | |
"\n", | |
"During simulation, a wave trace is output in `.vcd` format. These files can get very big and are not very scalable for extensive simulation dumps. To display them, a locally running tool is recommended. Below, a screen shot of a GTKWave trace for the above image encoding:\n", | |
"\n", | |
"" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "5709ce0e-c0ae-4c89-889a-9d7640a704da", | |
"metadata": {}, | |
"source": [ | |
"The hierarchy of the hardware is visible in the above `SST` tree display. There is no signal restriction for this simulation, all internal signals are made visible.\n", | |
"\n", | |
"Short explanation:\n", | |
"\n", | |
"* According to the JPEG_L2 IP documentation, a `jpeg_start` signal is required to arm the encoder before every start of the next image frame. This can be either handled by software or by a DMA engine.\n", | |
"* Once encoding is done, `jpeg_done` will go high until a new start condition asserts\n", | |
"* JPEG data comes in packet bursts from two channels (luma/chroma) simultaneously and thus requires internal FIFO buffering. With sane configuration and sufficient output clock speed, the packet rate does not normally exceed the FIFO capabilities. However, when such errors occur, `overrun` flags are monitored for both channels" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "095d1b2c-a862-444a-81ff-8d1b7e232e51", | |
"metadata": {}, | |
"source": [ | |
"## Co-simulation with other simulators\n", | |
"\n", | |
"To run such a simulation alongside another external simulation, specific `VPI` extensions are required to speak to such a simulator. This is in some cases limited. For this configuration, you are open to experiment with looped in Python routines within the cyrite co-simulation event handling domain, however it is not officially supported." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "a1758680-1066-4ec5-8866-8563f515e61c", | |
"metadata": {}, | |
"source": [ | |
"## Other IP options\n", | |
"\n", | |
"This encoder is featured by a few video extensions such as:\n", | |
"\n", | |
"* Direct synchronous bayer pattern to YCrCb 4:2:2 conversion, suited for color camera sensors\n", | |
"* Simple color correction (16 bit)\n", | |
"* CottonPicken legacy filter engine\n", | |
"* Streaming:\n", | |
" * Full SoC MJPEG over RTP/UDP (RFC2435) streaming solutions\n", | |
" * Custom streaming approach for greater image size and stereo formats (non-standardized)\n", | |
" * gstreamer setups for full low latency streaming\n", | |
"\n", | |
"This IP can be built as eval version on request to emit VHDL or Verilog code for integration evaluation." | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3 (ipykernel)", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.10.0" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment