├── lab4_supplementary_material ├── cmplx_test │ ├── CMakeLists.txt │ └── main.c └── fp_mul.sv ├── lab2_imgs ├── gtkwave.png ├── pattern_search.png └── signal_wave_display.png ├── lab3_imgs └── cpolcpha.png ├── lab4_imgs ├── RISC-V_base_opcode_map.png └── R-type_instruction_encoding.png ├── README.md ├── .github └── workflows │ └── main.yml ├── lab0preamble.md ├── lab3.md ├── lab1.md ├── lab2.md ├── lab4.md └── LICENSE /lab4_supplementary_material/cmplx_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_prog(cmplx_test main.c) 2 | -------------------------------------------------------------------------------- /lab2_imgs/gtkwave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowRISC/ibex-demo-system-labs/HEAD/lab2_imgs/gtkwave.png -------------------------------------------------------------------------------- /lab3_imgs/cpolcpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowRISC/ibex-demo-system-labs/HEAD/lab3_imgs/cpolcpha.png -------------------------------------------------------------------------------- /lab2_imgs/pattern_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowRISC/ibex-demo-system-labs/HEAD/lab2_imgs/pattern_search.png -------------------------------------------------------------------------------- /lab2_imgs/signal_wave_display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowRISC/ibex-demo-system-labs/HEAD/lab2_imgs/signal_wave_display.png -------------------------------------------------------------------------------- /lab4_imgs/RISC-V_base_opcode_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowRISC/ibex-demo-system-labs/HEAD/lab4_imgs/RISC-V_base_opcode_map.png -------------------------------------------------------------------------------- /lab4_imgs/R-type_instruction_encoding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowRISC/ibex-demo-system-labs/HEAD/lab4_imgs/R-type_instruction_encoding.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open RISC-V Silicon Development with Ibex 2 | 3 | List of labs for the Ibex Demo System: 4 | - [Lab 1: Programming your FPGA and interacting with the demo system](./lab1.md) 5 | - [Lab 2: Simulating with Verilator+GTKWave and modifying a HW peripheral](./lab2.md) 6 | - [Lab 3: Using the LCD Display over SPI](./lab3.md) 7 | - [Lab 4: Adding custom instructions to Ibex](./lab4.md) 8 | 9 | This content is copyright lowRISC and licensed under the Creative Commons Attribution 4.0 International Public License, see LICENSE for details. 10 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Generate PDF 2 | 3 | on: push 4 | 5 | jobs: 6 | convert_via_pandoc: 7 | runs-on: ubuntu-22.04 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: constrain width of some images for pandoc 11 | run: sed -i '/pattern_search\.png/ s/$/{width=200}/' lab2.md 12 | - name: create file list 13 | id: files_list 14 | run: | 15 | mkdir output # create output dir 16 | # this will also include README.md 17 | echo "::set-output name=files::$(printf '"%s" ' lab*.md)" 18 | - uses: docker://pandoc/latex:2.9 19 | with: 20 | args: --output=output/result.pdf ${{ steps.files_list.outputs.files }} -f markdown 21 | - uses: actions/upload-artifact@master 22 | with: 23 | name: output 24 | path: output 25 | -------------------------------------------------------------------------------- /lab4_supplementary_material/fp_mul.sv: -------------------------------------------------------------------------------- 1 | // Copyright lowRISC contributors. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | module fp_mul #( 6 | parameter int FP_EXP = 12, 7 | parameter int FP_MANT = 15, 8 | parameter bit CLAMP = 0 9 | ) ( 10 | input logic [FP_MANT:0] a_i, 11 | input logic [FP_MANT:0] b_i, 12 | output logic [FP_MANT:0] result_o 13 | ); 14 | localparam int MUL_RES_WIDTH = FP_MANT*2+2; 15 | 16 | logic [MUL_RES_WIDTH-1:0] mul_res_signed; 17 | logic [MUL_RES_WIDTH-1:0] mul_res_unsigned; 18 | 19 | assign mul_res_signed = $signed(a_i) * $signed(b_i); 20 | assign mul_res_unsigned = $unsigned(mul_res_signed); 21 | 22 | if (CLAMP) begin 23 | assign result_o = 24 | mul_res_unsigned[MUL_RES_WIDTH-1] & ~(&mul_res_unsigned[MUL_RES_WIDTH-2:FP_MANT+FP_EXP]) ? 25 | {1'b1, {FP_MANT{1'b0}}} : 26 | ~mul_res_unsigned[MUL_RES_WIDTH-1] & (|mul_res_unsigned[MUL_RES_WIDTH-2:FP_MANT+FP_EXP]) ? 27 | {1'b0, {FP_MANT{1'b1}}} : 28 | mul_res_unsigned[FP_MANT+FP_EXP:FP_EXP]; 29 | end else begin 30 | logic unused_mul_res_unsigned; 31 | assign unused_mul_res_unsigned = 32 | ^{mul_res_unsigned[MUL_RES_WIDTH-1:FP_MANT+FP_EXP+1], mul_res_unsigned[FP_EXP-1:0]}; 33 | 34 | assign result_o = mul_res_unsigned[FP_MANT+FP_EXP:FP_EXP]; 35 | end 36 | endmodule 37 | 38 | -------------------------------------------------------------------------------- /lab0preamble.md: -------------------------------------------------------------------------------- 1 | # Open RISC-V Silicon Development with Ibex 2 | 3 | In this tutorial, lowRISC shows how to work with a minimal SoC built around Ibex (the Ibex Demo System, https://github.com/lowRISC/ibex-demo-system). Ibex is a mature RISC-V (RV32IMCB) core which has seen several tape outs across academia and industry. lowRISC helps maintain Ibex as part of the OpenTitan project. The SoC is fully open source and designed to serve as a starting point for embedded computer architecture research, development, and teaching. The SoC combines Ibex with a number of useful peripherals (such as UART, Timer, PWM and SPI) and a full-featured debug interface. 4 | 5 | The SoC has been intentionally kept simple, so people new to hardware development and/or SoC design can quickly adapt it to their needs. The labs will use the affordable (160$) Digilent Arty A7 FPGA board. A ‘simulation first’ approach is taken (using the open source Verilator simulator) to get RTL designed and working before FPGA synthesis (which is done with Xilinx Vivado). This gives a quicker, less frustrating, develop/build/debug loop. 6 | 7 | [lowRISC](https://lowrisc.org/) is a not-for-profit company with a full stack engineering team based in Cambridge, UK. We use collaborative engineering to develop and maintain open source silicon designs and tools. Ibex development is funded by the OpenTitan project. 8 | 9 | This content is copyright lowRISC and licensed under the [Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/), see LICENSE for details. 10 | -------------------------------------------------------------------------------- /lab3.md: -------------------------------------------------------------------------------- 1 | # Lab 3: Using the LCD Display over SPI 2 | 3 | 4 | 5 | In this third lab, you will learn how to interface an LCD Display over a serial protocol (SPI), and you will draw a custom shape on the LCD display. 6 | 7 | 8 | ## Understanding the Serial Peripheral Interface (SPI) module 9 | 10 | SPI is a serial communication interface mainly used for short-distance communication between integrated circuits (ICs). 11 | SPI has two main variations, 4-wire and 3-wire. 12 | The Ibex demo system supports both, and our LCD screen uses the 3-wire version. 13 | This means the Ibex demo system receives and transmits data through the same wire (`SDA`). 14 | As SPI is not a formal standard, there is no formal standard of it. 15 | For this lab, we provide a simple SPI module to fit the requirements of an LCD screen. 16 | 17 | Depending on the two parameters Clock Polarity (`CPOL`) and Clock Phase (`CPHA`), SPI communication can occur in one of four different modes. 18 | We use default parameters of `CPOL=0` and `CPHA=1`, as required by the LCD screen. 19 | `CPOL=0` means that when there is no transmission, the serial clock (`SCK`) is LOW. 20 | `CPHA=1` means that data is transmitted at the positive edge of `SCK`. 21 | 22 | ![](./lab3_imgs/cpolcpha.png) 23 | 24 | The SPI block in the Ibex demo system consists of two modules inside the `spi_top` wrapper module: (1) the `prim_fifo_sync` first-in first-out (FIFO) module, which enables software to write data to be transmitted without having to wait for the transmission of individual bits, and (2) the `spi_host` module, which gets the data in 8-bit bytes from the FIFO and sends it bit by bit with a generated serial clock. 25 | Note that `spi_host` will request data from FIFO as long as it's not empty. 26 | 27 | The serial clock is generated by a simple counter. 28 | The width and the limit of the counter are determined by the verilog module parameters `ClockFrequency` and `BaudRate`, and they are currently set to generate a frequency of 12.5 MHz. 29 | When the `spi_host` module reads a byte from the FIFO, a second counter begins counting down from 7 to 0, which is the index of the bit in the byte that is being transmitted. Therefore the most significant bit is sent first. 30 | 31 | The hardware FIFO has a standard interface, consisting of: 32 | 33 | `clk,wdata,wdata_valid,rdata,rdata_valid,rdata_ready,full,empty` 34 | 35 | To fill up the FIFO and thus to send data using SPI, software needs to write to a register named `TX` at the address `0x80004000`, of which the lowest 8 bits is mapped to the `wdata` port. Software needs to write to this register byte by byte. 36 | There is also a status register named `STATUS_REG` at address `0x80004004` which has the fields for `full` at bit 0 and `empty` at bit 1. 37 | 38 | What might be some use cases for `FIFO_EMPTY` and `FIFO_FULL`? 39 | 40 | 41 | 42 | ## Drawing on the LCD display 43 | 44 | The LCD display is connected to the Ibex demo system via SPI. This particular display used can be found online by searching for "Arduino 1.8 TFT Display". To drive the display panel itself, we communicate with a driver chip on the board with the part number of ST7735. You can think about the driver chip as the API to the display, which is what our software will be written against. You can find the datasheet for it [here](https://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf). 45 | A display driver (under `vendor/display_drivers`) implements low-level functions to write commands and data serially to the LCD display. 46 | The commands and data formats are extracted from the data sheet of the LCD display module. 47 | On top of these low-level functions, the driver provides functions for initializing the display, setting the orientation, clearing it, and for drawing individual pixels, vertical and horizontal lines, rectangles, characters, strings, and bitmaps. 48 | 49 | Let's use the existing functions for drawing shapes to create a new function that draws a shape composed of horizontal and vertical rectangles. 50 | We suggest you complete the implementation of the following function template, which draws a cross defined by a center point, the length of one arm, and a color: 51 | ```c 52 | Result lcd_st7735_draw_cross(St7735Context *ctx, 53 | LCD_Point center, 54 | uint16_t length, 55 | uint32_t color); 56 | ``` 57 | Alternatively, you can implement the drawing function for a shape of your own choice. 58 | Create the function in `sw/demo/lcd_st7735/main.c`, and don't forget the local function declaration if needed. 59 | 60 | 61 | When you are done, you can see your implementation in action on the LCD by changing the action of the second menu item (i.e., the reaction to the press of button 1 in the menu) to 62 | ```c 63 | case BTN1: 64 | lcd_st7735_draw_cross(&lcd, (LCD_Point){.x = 80, .y = 80}, 10, BGRColorRed); 65 | timer_delay(1000); 66 | break; 67 | ``` 68 | Recompile the program, and execute it on the FPGA. Check that the shape drawn is what you expected! 69 | 70 | 71 | ## Rendering a Mandelbrot set 72 | 73 | As we can draw arbitrary bitmaps on the display, let's put Ibex to work calculating and drawing the [Mandelbrot set](https://en.wikipedia.org/wiki/Mandelbrot_set). 74 | The LCD demo program already includes the code for that, that should be the default shown on the display when loading the binary `sw/build/demo/lcd_st7735/lcd_st7735`. 75 | 76 | 77 | ## Measuring computation cycles and displaying them 78 | 79 | While admiring Ibex's work, we may wonder how many cycles the actual computations take. 80 | The demo includes a floating point implementation and a fixed point implementation. 81 | Ibex doesn't have hardware floating point support so software emulation is used instead. 82 | Because of this the fixed point implementation is faster than the floating point. 83 | To find out how much faster, let's use Ibex's cycle counter. 84 | For this, `demo_system.h` declares two functions to get the value of the cycle counter and to reset it, respectively: 85 | ```c 86 | uint32_t get_mcycle(void); 87 | void reset_mcycle(void); 88 | ``` 89 | 90 | Let's use these functions to count the number of cycles Ibex spends for calculations in the `fractal_mandelbrot_float` function: 91 | 92 | 1. Add `#include "demo_system.h"` near the top of `sw/demo/lcd_st7735/fractal_float.c`. 93 | 2. Append an argument `unsigned int *compute_cycles` to the `fractal_mandelbrot_float` function signature in `fractal_float.c` and `fractal.h`. 94 | 3. Initialize the cycle count to zero so it can be used for summing up; i.e., add `*compute_cycles = 0;` to the top of the body of `fractal_mandelbrot_float`. 95 | 4. Find the inner computation loop of the fractal. 96 | Put `reset_mcycle();` before it and `*compute_cycles += get_mcycle();` after it. 97 | 5. In the `fractal_test` function of `sw/demo/lcd_st7735/main.c`, declare a variable to hold the compute cycle count and pass it as last argument to `fractal_mandelbrot_float`. 98 | 6. After the call to `fractal_mandelbrot_float`, use the following snippet to print the number of cycles taken on the LCD: 99 | ```c 100 | unsigned int compute_cycles; 101 | fractal_mandelbrot_float(lcd, &compute_cycles); 102 | char buf[32]; 103 | snprintf(buf, 32, "cycles: %10d", compute_cycles); 104 | lcd_st7735_puts(lcd, (LCD_Point){.x = 0, .y = 112}, buf); 105 | timer_delay(5000); 106 | ``` 107 | 7. Add `#include ` near the top of `sw/demo/lcd_st7735/main.c`. 108 | 8. Recompile the program and execute it on the FPGA. 109 | 110 | 111 | Now do the same for the fixed point implementation (`fractal_mandelbrot_fixed` in `sw/demo/lcd_st7735/fractal_fixed.c`). 112 | 113 | How many cycles does it take Ibex to compute the fractal for the floating and fixed point versions? 114 | 115 | -------------------------------------------------------------------------------- /lab4_supplementary_material/cmplx_test/main.c: -------------------------------------------------------------------------------- 1 | // Copyright lowRISC contributors. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "demo_system.h" 6 | 7 | #define FP_EXP 12 8 | #define FP_MANT 15 9 | #define MAKE_FP(i, f, f_bits) ((i << FP_EXP) | (f << (FP_EXP - f_bits))) 10 | 11 | typedef uint32_t cmplx_packed_t; 12 | 13 | static inline cmplx_packed_t cmplx_mul_insn(uint32_t a, uint32_t b) { 14 | uint32_t result; 15 | 16 | asm (".insn r CUSTOM_0, 0, 0, %0, %1, %2" : 17 | "=r"(result) : 18 | "r"(a), "r"(b)); 19 | 20 | return result; 21 | } 22 | 23 | static inline cmplx_packed_t cmplx_add_insn(uint32_t a, uint32_t b) { 24 | uint32_t result; 25 | 26 | asm (".insn r CUSTOM_0, 1, 0, %0, %1, %2" : 27 | "=r"(result) : 28 | "r"(a), "r"(b)); 29 | 30 | return result; 31 | } 32 | 33 | static inline int32_t cmplx_abs_sq_insn(uint32_t a) { 34 | int32_t result; 35 | 36 | asm (".insn r CUSTOM_0, 2, 0, %0, %1, x0" : 37 | "=r"(result) : 38 | "r"(a)); 39 | 40 | return result; 41 | } 42 | 43 | typedef struct { 44 | int32_t real; 45 | int32_t imag; 46 | } cmplx_t; 47 | 48 | cmplx_t test_nums[] = { 49 | {MAKE_FP(1, 0, 0), 0}, 50 | {0, MAKE_FP(1, 0, 0)}, 51 | 52 | {MAKE_FP(2, 0, 0), MAKE_FP(1, 0, 0)}, 53 | {MAKE_FP(1, 0, 0), MAKE_FP(2, 0, 0)}, 54 | 55 | {MAKE_FP(2, 0, 0), MAKE_FP(1, 0, 0)}, 56 | {-MAKE_FP(1, 0, 0), -MAKE_FP(2, 0, 0)}, 57 | 58 | {MAKE_FP(4, 0, 0), -MAKE_FP(1, 0, 0)}, 59 | {MAKE_FP(1, 0, 0), MAKE_FP(0, 1, 1)}, 60 | 61 | {MAKE_FP(7, 0xFFF, 12), -MAKE_FP(8, 0x0, 0)}, 62 | {-MAKE_FP(8, 0x0, 0), MAKE_FP(7, 0xFFF, 12)}, 63 | 64 | {MAKE_FP(7, 0xFFF, 12), 0}, 65 | {0, -MAKE_FP(7, 0xFFF, 12)}, 66 | 67 | {-MAKE_FP(3, 0x123, 12), MAKE_FP(2, 0x1b, 5)}, 68 | {-MAKE_FP(1, 0x2d, 6), -MAKE_FP(0, 0x87, 8)}, 69 | 70 | {MAKE_FP(4, 0, 0), MAKE_FP(4, 0, 8)}, 71 | {-MAKE_FP(2, 0, 0), MAKE_FP(2, 0, 0)}, 72 | 73 | {MAKE_FP(4, 0xD8F, 12), -MAKE_FP(0, 0xff, 8)}, 74 | {-MAKE_FP(2, 0x6c, 7), MAKE_FP(3, 0x3a5, 10)} 75 | }; 76 | 77 | #define NUM_TESTS 9 78 | 79 | int32_t fp_clamp(int32_t x) { 80 | if ((x < 0) && (x < -(1 << FP_MANT))) { 81 | return -(1 << FP_MANT); 82 | } 83 | 84 | if ((x > 0) && (x >= (1 << FP_MANT))) { 85 | return (1 << FP_MANT) - 1; 86 | } 87 | 88 | return x; 89 | } 90 | 91 | int32_t fp_trunc(int32_t x) { 92 | uint32_t res = x & 0xffff; 93 | 94 | if (res & 0x8000) { 95 | return 0xffff0000 | res; 96 | } 97 | 98 | return res; 99 | } 100 | 101 | int32_t to_fp(int32_t x) { 102 | int32_t res; 103 | res = fp_trunc(x << FP_EXP); 104 | 105 | return res; 106 | } 107 | 108 | int32_t fp_add(int32_t a, int32_t b) { 109 | return fp_trunc(a + b); 110 | } 111 | 112 | int32_t fp_mul(int32_t a, int32_t b) { 113 | return fp_trunc((a * b) >> FP_EXP); 114 | } 115 | 116 | int32_t fp_mul_clamp(int32_t a, int32_t b) { 117 | return fp_clamp((a * b) >> FP_EXP); 118 | } 119 | 120 | int32_t cmplx_abs_sq(cmplx_t c) { 121 | return fp_mul_clamp(c.real, c.real) + fp_mul_clamp(c.imag, c.imag); 122 | } 123 | 124 | cmplx_t cmplx_mul(cmplx_t c1, cmplx_t c2) { 125 | cmplx_t res; 126 | 127 | res.real = fp_add(fp_mul(c1.real, c2.real), -fp_mul(c1.imag, c2.imag)); 128 | res.imag = fp_add(fp_mul(c1.real, c2.imag), fp_mul(c1.imag, c2.real)); 129 | 130 | return res; 131 | } 132 | 133 | cmplx_t cmplx_sq(cmplx_t c) { 134 | cmplx_t res; 135 | 136 | res.real = fp_add(fp_mul(c.real, c.real), -fp_mul(c.imag, c.imag)); 137 | res.imag = fp_mul(to_fp(2), fp_mul(c.real, c.imag)); 138 | 139 | return res; 140 | } 141 | 142 | cmplx_t cmplx_add(cmplx_t c1, cmplx_t c2) { 143 | cmplx_t res; 144 | 145 | res.real = fp_add(c1.real, c2.real); 146 | res.imag = fp_add(c1.imag, c2.imag); 147 | 148 | return res; 149 | } 150 | 151 | cmplx_packed_t pack_cmplx(cmplx_t c) { 152 | return ((c.real & 0xffff) << 16) | (c.imag & 0xffff); 153 | } 154 | 155 | cmplx_t unpack_cmplx(cmplx_packed_t c) { 156 | cmplx_t result; 157 | 158 | result.real = ((int32_t)c) >> 16; 159 | result.imag = c & 0xffff; 160 | if (result.imag & 0x8000) { 161 | result.imag |= 0xffff0000; 162 | } 163 | 164 | return result; 165 | } 166 | 167 | void dump_binop_result(cmplx_t c1, cmplx_t c2, cmplx_packed_t c1_packed, 168 | cmplx_packed_t c2_packed, cmplx_t result, cmplx_packed_t result_packed) { 169 | puts("C1\n"); 170 | puthex(c1.real); 171 | puts("\n"); 172 | puthex(c1.imag); 173 | puts("\n"); 174 | puts("\n"); 175 | 176 | puts("C2\n"); 177 | puthex(c2.real); 178 | puts("\n"); 179 | puthex(c2.imag); 180 | puts("\n"); 181 | puts("\n"); 182 | 183 | puts("C1 Packed\n"); 184 | puthex(c1_packed); 185 | puts("\n"); 186 | puts("\n"); 187 | 188 | puts("C2 Packed\n"); 189 | puthex(c2_packed); 190 | puts("\n"); 191 | puts("\n"); 192 | 193 | puts("Result (from soft cmplx)\n"); 194 | puthex(result.real); 195 | puts("\n"); 196 | puthex(result.imag); 197 | puts("\n"); 198 | puts("\n"); 199 | 200 | puts("Result Packed (from hard cmplx insn)\n"); 201 | puthex(result_packed); 202 | puts("\n"); 203 | puts("\n"); 204 | } 205 | 206 | int run_mul_test(cmplx_t c1, cmplx_t c2, int dump_result) { 207 | cmplx_packed_t c1_packed, c2_packed, result_packed; 208 | 209 | c1_packed = pack_cmplx(c1); 210 | c2_packed = pack_cmplx(c2); 211 | 212 | result_packed = cmplx_mul_insn(c1_packed, c2_packed); 213 | 214 | cmplx_t result; 215 | result = cmplx_mul(c1, c2); 216 | 217 | cmplx_t result_unpacked; 218 | result_unpacked = unpack_cmplx(result_packed); 219 | 220 | if (dump_result) { 221 | dump_binop_result(c1, c2, c1_packed, c2_packed, result, result_packed); 222 | } 223 | 224 | if (result_unpacked.real != result.real) { 225 | return 0; 226 | } 227 | 228 | if (result_unpacked.imag != result.imag) { 229 | return 0; 230 | } 231 | 232 | return 1; 233 | } 234 | 235 | int run_add_test(cmplx_t c1, cmplx_t c2, int dump_result) { 236 | cmplx_packed_t c1_packed, c2_packed, result_packed; 237 | 238 | c1_packed = pack_cmplx(c1); 239 | c2_packed = pack_cmplx(c2); 240 | 241 | result_packed = cmplx_add_insn(c1_packed, c2_packed); 242 | 243 | cmplx_t result; 244 | result = cmplx_add(c1, c2); 245 | 246 | cmplx_t result_unpacked; 247 | result_unpacked = unpack_cmplx(result_packed); 248 | 249 | if (dump_result) { 250 | dump_binop_result(c1, c2, c1_packed, c2_packed, result, result_packed); 251 | } 252 | 253 | if (result_unpacked.real != result.real) { 254 | return 0; 255 | } 256 | 257 | if (result_unpacked.imag != result.imag) { 258 | return 0; 259 | } 260 | 261 | return 1; 262 | } 263 | 264 | int run_abs_sq_test(cmplx_t c, int dump_results) { 265 | cmplx_packed_t c_packed; 266 | 267 | c_packed = pack_cmplx(c); 268 | 269 | int32_t result_soft; 270 | result_soft = cmplx_abs_sq(c); 271 | 272 | int32_t result_hard; 273 | result_hard = cmplx_abs_sq_insn(c_packed); 274 | 275 | if (dump_results) { 276 | puts("C\n"); 277 | puthex(c.real); 278 | puts("\n"); 279 | puthex(c.imag); 280 | puts("\n"); 281 | puts("\n"); 282 | 283 | puts("C Packed\n"); 284 | puthex(c_packed); 285 | puts("\n"); 286 | puts("\n"); 287 | 288 | puts("Result (from soft cmplx)\n"); 289 | puthex(result_soft); 290 | puts("\n"); 291 | 292 | puts("Result (from hard cmplx)\n"); 293 | puthex(result_hard); 294 | puts("\n"); 295 | } 296 | 297 | if (result_hard != result_soft) { 298 | return 0; 299 | } 300 | 301 | return 1; 302 | } 303 | 304 | int main(void) { 305 | int failures = 0; 306 | 307 | for (int i = 0;i < NUM_TESTS; ++i) { 308 | if (!run_mul_test(test_nums[i * 2], test_nums[i * 2 + 1], 0)) { 309 | puts("Mul test failed: "); 310 | puthex(i); 311 | puts("\n"); 312 | run_mul_test(test_nums[i * 2], test_nums[i * 2 + 1], 1); 313 | puts("\n\n"); 314 | ++failures; 315 | } 316 | 317 | if (!run_add_test(test_nums[i * 2], test_nums[i * 2 + 1], 0)) { 318 | puts("Add test failed: "); 319 | puthex(i); 320 | puts("\n"); 321 | run_add_test(test_nums[i * 2], test_nums[i * 2 + 1], 1); 322 | puts("\n\n"); 323 | ++failures; 324 | } 325 | } 326 | 327 | for (int i = 0;i < NUM_TESTS * 2; ++i) { 328 | if (!run_abs_sq_test(test_nums[i], 0)) { 329 | puts("AbsSq test failed: "); 330 | puthex(i); 331 | puts("\n"); 332 | run_abs_sq_test(test_nums[i], 1); 333 | puts("\n\n"); 334 | ++failures; 335 | } 336 | } 337 | 338 | if (failures) { 339 | puthex(failures); 340 | puts(" failures seen\n"); 341 | } else { 342 | puts("All tests passed!\n"); 343 | } 344 | 345 | return 0; 346 | } 347 | -------------------------------------------------------------------------------- /lab1.md: -------------------------------------------------------------------------------- 1 | # Lab 1: Programming your FPGA and interacting with the demo system 2 | 3 | Welcome to the first lab on using the [Ibex demo system](https://github.com/lowRISC/ibex-demo-system). In this lab, we will: 4 | 5 | - Set up your development environment. 6 | - Learn how to build the software. 7 | - Program our board with a bitstream. 8 | - Run the software on the board. 9 | - Read from the UART. 10 | - Interact with the Ibex using GDB. 11 | 12 | ## Getting started 13 | To start please decompress the zip file on the USB or clone the repository using the following terminal commands: 14 | ```bash 15 | git clone https://github.com/lowRISC/ibex-demo-system 16 | cd ibex-demo-system 17 | ``` 18 | 19 | Now, let us set up our container. First install docker (or you can use pacman if you are already familiar with it. 20 | On the USB go to the "Docker Installers" directory and follow the instructions based on your operating system. 21 | 22 | ### Windows 23 | Check that you have virtualization enabled in task manager under performance and CPU. If not, you need to enable virtualization in your BIOS settings. 24 | Make sure "Windows Subsystem for Linux" and "Virtual Machine Platform" are enabled in "Turn Windows features on or off" 25 | Double click wsl_update_x86.msi and install. 26 | Open powershell and run: wsl --set-default-version 2 27 | Double click "Docker Desktop Installer Windows.exe" 28 | When prompted select WSL 2. 29 | If your admin account is different to your user account, you must add the user to the docker-users group. Run Computer Management as an administrator and navigate to Local Users and Groups > Groups > docker-users. Right-click to add the user to the group. Log out and log back in for the changes to take effect. 30 | 31 | ### Mac 32 | Double click DockerMacIntel.dmg or DockerMackArm.dmg depending on your CPU and drag the Docker icon to the Applications folder. 33 | Double click Docker.app in the Applications folder. 34 | Accept the terms. 35 | 36 | ### Ubuntu 37 | Make sure you have gnome-terminal installed 38 | ```bash 39 | sudo apt-get install ./docker-desktop-4.16.0-ubuntu-amd64.deb 40 | systemctl --user enable docker-desktop 41 | ``` 42 | 43 | ### Debian 44 | Make sure you have gnome-terminal installed 45 | ```bash 46 | sudo apt-get install ./docker-desktop-4.16.0-debian-amd64.deb 47 | systemctl --user enable docker-desktop 48 | ``` 49 | 50 | ### Fedora 51 | Make sure you have gnome-terminal installed 52 | ```bash 53 | sudo dnf install ./docker-desktop-4.16.0-fedora-x86_64.rpm 54 | systemctl --user start docker-desktop 55 | ``` 56 | 57 | ### Set up container (Linux/Mac) 58 | There is a prebuilt container of tools available you may want to use to get started quickly. 59 | There are instructions for building the container for either Docker/Podman located in ./container/README.md. 60 | 61 | A container image may be provided to you on a USB stick. You can load the container file by running : 62 | ```bash 63 | sudo docker load < ibex_demo_image.tar 64 | # OR 65 | podman load < ibex_demo_image.tar 66 | ``` 67 | 68 | If you already have a container file, you can start the container by running : 69 | ```bash 70 | sudo docker run -it --rm \ 71 | -p 6080:6080 \ 72 | -p 3333:3333 \ 73 | -v $(pwd):/home/dev/demo:Z \ 74 | ibex 75 | ``` 76 | OR 77 | ```bash 78 | podman unshare chown 1000:1000 -R . 79 | podman run -it --rm \ 80 | -p 6080:6080 \ 81 | -p 3333:3333 \ 82 | -v $(pwd):/home/dev/demo:Z \ 83 | ibex 84 | podman unshare chown 0:0 -R . 85 | ``` 86 | To access the container once running, go to [http://localhost:6080/vnc.html](http://localhost:6080/vnc.html). 87 | 88 | ### Set up container (Windows) 89 | Run a command prompt in administrator mode and type: 90 | ```bash 91 | cd "C:\Program Files\Docker\Docker" 92 | .\DockerCli.exe -SwitchLinuxEngine 93 | ``` 94 | 95 | Go to the folder on the USB named "Docker Images" and run: 96 | ```bash 97 | docker load -i ibex_demo_image.tar 98 | ``` 99 | 100 | Go to the folder where you have decompressed the demo system repository: 101 | ```bash 102 | docker run -it --rm -p 6080:6080 -p 3333:3333 -v %cd%:/home/dev/demo:Z ibex 103 | ``` 104 | 105 | ## Building software 106 | To build the software use the following commands in your terminal: 107 | ```bash 108 | cd /home/dev/demo 109 | mkdir -p sw/build 110 | pushd sw/build 111 | cmake .. 112 | make 113 | popd 114 | ``` 115 | 116 | This builds the software that we can later use as memory content for the Ibex running in the demo system. For example, the binary for the demo application is located at `sw/build/demo/hello_world/demo`. 117 | 118 | ## Getting the FPGA bitstream 119 | Get the FPGA bitstream off of the USB or download the [FPGA bitstream from GitHub](https://github.com/lowRISC/ibex-demo-system/releases/download/v0.0.2/lowrisc_ibex_demo_system_0.bit). Put the bitstream at the root of your demo system repository. 120 | 121 | Alternatively, you can build your own bitstream if you have access to Vivado by following the instructions in [the README](https://github.com/lowRISC/ibex-demo-system/blob/main/README.md). 122 | 123 | ## Programming the FPGA 124 | Before we can program the FPGA, we need to make it accessible from inside to the container. 125 | First, lets find out which bus and device our Arty is on: 126 | ```console 127 | $ lsusb 128 | ... 129 | Bus 00X Device 00Y: ID 0403:6010 Future Technology Devices International, Ltd FT2232C/D/H Dual UART/FIFO IC 130 | ... 131 | ``` 132 | Where X and Y are numbers. Please note down what X and Y is for you (this will change if you unplug and replug your FPGA). 133 | 134 | Then exit your docker and run it with the following parameters (assuming you're running Linux): 135 | ```bash 136 | sudo docker run -it --rm \ 137 | -p 6080:6080 \ 138 | -p 3333:3333 \ 139 | -v $(pwd):/home/dev/demo:Z \ 140 | --privileged \ 141 | --device=/dev/bus/usb/00X/00Y \ 142 | --device=/dev/ttyUSB1 \ 143 | ibex 144 | ``` 145 | 146 | Then inside the container at [localhost:6080/vnc.html](http://localhost:6080/vnc.html), we program the FPGA with the following terminal command: 147 | ```bash 148 | openFPGALoader -b arty_a7_35t \ 149 | /home/dev/demo/lowrisc_ibex_demo_system_0.bit 150 | ``` 151 | 152 | ## Loading the software 153 | Before we load the software, please check that you have OpenOCD installed: 154 | ```bash 155 | openocd --version 156 | ``` 157 | Please also check that you have version 0.11.0. 158 | 159 | Then you can load and run the program as follows: 160 | ```bash 161 | util/load_demo_system.sh run ./sw/build/demo/hello_world/demo 162 | ``` 163 | 164 | Congratulations! You should now see the green LEDs zipping through and the RGB LEDs dimming up and down with different colors each time. 165 | 166 | ## Interacting with the UART 167 | Besides the LEDs, the demo application also prints to the UART serial output. You can see this output using the following command: 168 | ```bash 169 | screen /dev/ttyUSB1 115200 170 | ``` 171 | If you see an immediate `[screen is terminating]`, it may mean that you need super user rights. In this case, you may try adding `sudo` before the `screen` command. To exit from the `screen` command, you should press control and a together, then release these two keys and press d. 172 | 173 | ### Exercise 1 174 | While the demo application is running try toggling the switches and buttons, you should see changes in the input value that is displayed by `screen`. 175 | 176 | ### Exercise 2 177 | Adjust the demo system application to print "Hello Ibex" instead of "Hello World". You should adjust the content of the file in `sw/demo/hello_world/main.c`. Afterwards, you should rebuild the software, but do not need to rebuild the bitstream. Once you've built your updated software, you can load the software onto the board to see the result. 178 | 179 | ## Debugging using GDB 180 | We can use OpenOCD to connect to the JTAG on the FPGA. We can then use GDB to connect to OpenOCD and interact with the board as we would when we debug any other application. 181 | 182 | First, let's load the software in the halted state: 183 | ```bash 184 | util/load_demo_system.sh halt ./sw/build/demo/hello_world/demo 185 | ``` 186 | 187 | In a separate terminal window, you can connect GDB to the OpenOCD server: 188 | ```bash 189 | riscv32-unknown-elf-gdb -ex "target extended-remote localhost:3333" \ 190 | ./sw/build/demo/hello_world/demo 191 | ``` 192 | 193 | Some useful GDB commands: 194 | 195 | - `step`: steps to the next instruction. 196 | - `advance main`: keep running until the start of the main function (you can replace main with another function). 197 | - `set {int}0xhex_addr = 0xhex_val`: write `hex_val` to the memory address `hex_addr`. 198 | - `x/w 0xhex_addr`: read a word (32 bits) from `hex_addr` and display it in hexidecimal. 199 | - `info locals`: shows you the values of the local variables for the current function. 200 | - `backtrace`: shows you the call stack. 201 | - `help`: to find commands you may not yet know. 202 | 203 | ### Exercise 3 204 | Write to the green LEDs using GDB. Look in `sw/common/demo_system_regs.h` for the value of `GPIO_BASE`. Use the set command above to write `0xa0` to this base address. This should change the green LEDs to be on, off, on and off. 205 | -------------------------------------------------------------------------------- /lab2.md: -------------------------------------------------------------------------------- 1 | # Lab 2: Simulating with Verilator+GTKWave and modifying a HW peripheral 2 | 3 | 4 | 5 | In this second lab, you will learn how to modify a hardware peripheral and how to simulate the system with Verilator and inspect the simulation waves with GTKWave. 6 | 7 | ## Simulating with Verilator 8 | 9 | [Verilator](https://verilator.org) is a widely-used simulator for Verilog and SystemVerilog. 10 | It compiles RTL code to a multithreaded C++ executable, which makes Verilator much faster than other simulators in many cases. 11 | Verilator is free software (licensed LGPLv3). 12 | If you have not installed Verilator yet, please [install it now](https://verilator.org/guide/latest/install.html). 13 | 14 | Next, build the Verilator simulation executable of the Ibex demo system by invoking FuseSoC in the root directory of the demo system: 15 | ``` 16 | fusesoc --cores-root=. run --target=sim --tool=verilator \ 17 | --setup --build lowrisc:ibex:demo_system 18 | ``` 19 | 20 | This will create an executable at 21 | ``` 22 | ./build/lowrisc_ibex_demo_system_0/sim-verilator/Vibex_demo_system 23 | ``` 24 | 25 | Now start the simulation by invoking that executable with two flags: the first flag enables the capturing of wave traces, and the second flag loads the `demo` binary into program memory 26 | ``` 27 | ./build/lowrisc_ibex_demo_system_0/sim-verilator/Vibex_demo_system \ 28 | -t --meminit=ram,$(pwd)/sw/build/demo/hello_world/demo 29 | ``` 30 | 31 | The simulation will run until you press `Ctrl+C`. 32 | As no outputs (such as UART or GPIO) are mapped to the Verilator console, the console output does not indicate much activity. 33 | However, the simulation is actively running and wave traces are continuously being written to a file called `sim.fst`. 34 | Let's inspect them. 35 | 36 | 37 | ## Inspecting waves with GTKWave 38 | 39 | [GTKWave](https://gtkwave.sourceforge.net/gtkwave.pdf) is a free (licensed GPLv2) wave viewer that supports many file formats. 40 | If you have not installed it yet, please do so now (preferrably using your distribution's package manager, alternatively by building from source following the instructions in the linked manual). 41 | Start GTKWave through your desktop's launcher or by entering `gtkwave` in a new shell. 42 | Then open the trace file of our simulation by clicking **File** -> **Open New Tab** and selecting `sim.fst` inside the root directory of the Ibex demo repository. 43 | 44 | A GTKWave tab has two main parts: the signal search tree (SST) on the left (green rectangle in screenshot below) and the signal wave display on the right (blue rectangle). 45 | 46 | ![](./lab2_imgs/gtkwave.png) 47 | 48 | Navigate the tree of instances by expanding an instance (clicking the plus symbol left of the instance name) to expose the list of instances inside. 49 | For example, the instantiated `ibex_demo_system` module resides at `TOP` -> `ibex_demo_system`. 50 | Inside that, you can find our peripherals (`u_gpio`, `u_pwm`, `u_spi`, and `u_timer`), the on-chip memory (`u_ram`), and the Ibex core (`u_top` -> `u_ibex_core`). 51 | 52 | Selecting an instance (clicking the instance name) shows the signals of that instance in the lower part of the SST. 53 | Double-clicking on a signal appends it to the signal wave display on the right. 54 | 55 | ### Ibex instruction interface 56 | Let's find out what instructions Ibex is fetching. 57 | Select the `u_ibex_core` instance and first add the `clk_i` and `rst_ni` signals. 58 | (It is good practice to always add those signals when inspecting the waves of a synchronous design.) 59 | Then add the following signals of the instruction request ports 60 | - `instr_req_o` 61 | - `instr_gnt_i` 62 | - `instr_addr_o` 63 | 64 | as well as these signals of the instruction response ports 65 | - `instr_rvalid_i` 66 | - `instr_err_i` 67 | - `instr_rdata_i` 68 | 69 | You can optionally group the signals by clicking **Edit** -> **Create Group** (or pressing `G`). 70 | This can be useful to visually identify which signals belong together. 71 | 72 | When you are done, your signal wave display should look similar to this: 73 | ![](./lab2_imgs/signal_wave_display.png) 74 | 75 | Now use the signal waves to find the first instruction requested by Ibex. 76 | The instruction fetch protocol stipulates that a request occurs when `instr_req_o` and `instr_gnt_i` are both high on a rising clock edge (and outside the active-low reset, of course). 77 | Clicking on a signal name and using the *Find Next Edge* functionality (the arrow pointing to the right in the toolbar) can help. 78 | If needed, you can zoom in and out on the time axis by clicking the magnifier glass `+`/`-` icons in the toolbar or pressing the keyboard shortcuts listed in the **Time** -> **Zoom** menu. 79 | What is the address of the first instruction, and what is its returned value? 80 | 81 | 82 | When observing instructions in wave traces, it often makes sense to correlate them with the disassembled binary. 83 | To disassemble the binary, execute 84 | ``` 85 | riscv32-unknown-elf-objdump -d sw/build/demo/hello_world/demo 86 | ``` 87 | 88 | Search the output of that command for the instruction address (first column). 89 | The instruction value (second column) should match what you observed in the wave trace. 90 | What does the instruction do? 91 | 92 | 93 | Note that the instruction *fetched* in any given cycle is not the same as the instruction *executed* in that cycle, because Ibex is a pipelined CPU. 94 | To correlate instructions with outputs of the CPU (e.g., to the register file or the memory), it is thus useful to know the program counter (PC) of the Execute stage. 95 | This PC is easily accessible under `u_top` -> `crash_dump_o` (in the tree, not in the signal list) -> `current_pc`. 96 | We recommend that you add this signal to the wave display. 97 | 98 | ### Ibex data interface 99 | 100 | Another important part for understanding Ibex's actions in the system is the data interface, through which Ibex accesses memories and memory-mapped peripherals. 101 | Add the `data_req_o`, `data_gnt_i`, `data_addr_o`, `data_we_o`, `data_wdata_o`, and `data_be_o` signals of the request ports and the `data_rvalid_i`, `data_rdata_i`, and `data_err_i` signals of the response ports. 102 | 103 | What is the first address that Ibex accesses after reset? 104 | Is this access a read or a write? 105 | 106 | 107 | Which instruction is executed when this memory access happens? 108 | (Hint: Obtain the instruction address from `current_pc` and search for that address in the disassembled binary.) 109 | What does the basic block containing the instruction do? 110 | (Hint: A basic block is a sequence of instructions with no branches in (except to the entry) or out (except at the exit).) 111 | 112 | 113 | Investigating the system memory map, which is defined by the `localparam`s near the top of `rtl/system/ibex_demo_system.sv`, we can see that the address belongs to the on-chip memory. 114 | Not all data addresses correspond to memory, however. 115 | The next entry in the address map shows that the GPIO peripheral has a space of 4 KiB starting at `32'h8000_0000` (and thus ending at `32'h8000_0FFF`). 116 | 117 | We know that the demo program should access the GPIO peripheral, so let's identify that access in the waves. 118 | For such a task, GTKWave has a very useful function that allows searching the waves for values. 119 | Select `data_req_o`, `data_gnt_i`, and `data_addr_o` in the signal wave display, and then click **Search** -> **Pattern Search 1**. 120 | The window that opens defines matches in two steps: firstly defining the conditions for individual signals, and secondly defining the relation of those conditions. 121 | We are interested in the case when `data_req_o` and `data_gnt_i` are high and `data_addr_o` has a hexadecimal value of `80000000`. 122 | To this end, make the following settings: 123 | 124 | ![](./lab2_imgs/pattern_search.png) 125 | 126 | Click the **Fwd** button to advance the cursor to Ibex's first access of the GPIO. 127 | What does it do? 128 | 129 | 130 | Then add the relevant signals from inside the GPIO module (`ibex_demo_system` -> `u_gpio`) to ascertain that the GPIO module does what Ibex wants. 131 | 132 | As you have now put quite some effort into configuring the wave display, it may make sense to save it. 133 | To do so, click **File** -> **Write Save File**. 134 | 135 | 136 | ## Adding a shift register to the GPIO peripheral 137 | 138 | As you have just noticed, the software interface of the GPIO peripheral is very simple. 139 | Let's extend its functionality by integrating a shift register specified as follows: 140 | - A write to offset `0xC` of the GPIO peripheral shifts the current bits of the GPIO output up by one position and sets the least-significant bit (LSB) of the shift register to the LSB of the value written. 141 | - A read to offset `0xC` of the GPIO peripheral returns the current bits of the GPIO output. 142 | 143 | Modify the RTL code of the GPIO peripheral (under `rtl/system/gpio.sv`) to implement this functionality. 144 | We recommend doing this in two steps: 145 | 1. Implement the address decoder for accessing the shift register (you can get inspiration from the existing address decoder code). 146 | 2. Implement the shift register functionality in the sequential block that sets the value of `gp_o`. 147 | This needs to be conditional on the address decoding implemented in the first step and use the data written by Ibex (`device_wdata_i`) as well as the current value of the GPIO output (`gp_o`). 148 | Hint: If you want to assign to `a` the concatenation of `b` and `c` you can do so with the following code: `a <= {b, c};` 149 | 150 | 151 | When you are done, rebuild the Verilator simulation executable. 152 | 153 | To enable this feature in the `demo` program, change the `0` in `#define USE_GPIO_SHIFT_REG 0` near the top of `sw/demo/hello_world/main.c` to `1` and recompile the `demo` binary. 154 | 155 | Rerun the simulation, reload the wave traces (clicking the circular arrow button in the toolbar or **File** -> **Reload Waveform**), and add the relevant waves to check that your GPIO shift register works as expected. 156 | -------------------------------------------------------------------------------- /lab4.md: -------------------------------------------------------------------------------- 1 | # Lab 4: Adding Custom Instructions to Ibex 2 | 3 | In Lab 3 we saw the difference in performance between the fixed and floating point Mandelbrot implementations, can we get further performance improvements? 4 | By adding custom RISC-V instructions, we can create dedicated hardware for computing the Mandelbrot set even more efficiently. 5 | This lab covers the process of adding such custom instructions, though before we discuss how to do that, let's first take a quick look at how you calculate a Mandelbrot set. 6 | 7 | ## Drawing the Mandelbrot set 8 | 9 | This is not a mathematics lab, so we're not going into lots of detail here nor does it matter if you don't really understand it. We're just going to cover the 'nuts and bolts' of the calculation you need to work with, the lowest-level math operations that dominate the runtime of the algorithm. 10 | Before we can draw the Mandelbrot set, we first need to be able to work with complex numbers. 11 | 12 | ### Complex Number Intro 13 | 14 | A complex number can be written as $a + bi$, where $a$ and $b$ are real numbers and $i = \sqrt{-1}$ is the *imaginary unit*. (We call it imaginary because there is no physical way to visualize what $\sqrt{-1}$ is!) 15 | $a$ is called the *real part*, and $b$ is called the *imaginary part*. 16 | 17 | To add complex numbers, simply sum the real and imaginary parts: 18 | 19 | $$(a + bi) + (c + di) = (a + c) + (b + d)i$$ 20 | 21 | To mutiply complex numbers, the components are multiplied by standard algebraic rules (where multiplication distributes over addition): 22 | 23 | $$(a + bi) (c + di) = ac + adi + bci + bdi^2 = (ac - bd) + (ad + bc)i$$ 24 | 25 | Noting that $i^2 = (\sqrt{-1})^2 = -1$. 26 | 27 | Finally, the absolute value $|C|$ of a complex number $C = a + bi$ (you can also think this as the magnitude of a 2D vector in the real and imaginary axis) is defined as: 28 | 29 | $$|C|^2 = a^2 + b^2$$ 30 | 31 | Noting we would need a square root to get the absolute value, but for our purposes the squared value is fine. 32 | 33 | ### Mandelbrot Set Calculation 34 | 35 | The Mandelbrot set is a set of complex numbers. 36 | A number $C$ is in the set if the recurrance relationship below never diverges to infinity (i.e., $|Z_n|$ is a finite number for all $n$). 37 | 38 | $$Z_0 = C$$ 39 | 40 | $$Z_{n+1} = Z_n^2 + C$$ 41 | 42 | It can be shown that if $|Z_n| > 2$ for any $n$, then the recurrence will diverge and hence $C$ is not part of the Mandelbrot set. 43 | 44 | To draw the set, we map pixels to real and imaginary numbers and calculate a certain number of iterations of the $Z_n$ recurrence for each point to decide if the point is in the set: 45 | 46 | - If $|Z_n| \leq 2$ (noting we can just test the square result against 4 to avoid a square root) for all iterations, we declare the point *in* the set. 47 | - If $|Z_n| > 2$ for any iteration, we terminate the iterations there and declare the point *not* in the set. 48 | 49 | We finally colour the result based upon the number of iterations we reached before making our decision. 50 | 51 | ## Complex number custom instructions 52 | 53 | Note: These instructions are tailored to this lab, and a 'real' complex number extension may do things differently, in particular the clamping and truncation behaviour discussed below. 54 | 55 | Our fixed point implementation of the Mandelbrot set renderer uses 16-bit numbers (12 fractional bits, 4 integer bits with 2s complement representation). 56 | This means we can pack a complex number into a single 32-bit number. 57 | The imaginary part will be placed in the lower bits (indexed `[15:0]` and the real part in upper bits (`[31:16]`). 58 | So how about some custom instructions that implement complex number operations on the packed 32 bit representation? 59 | 60 | We are going to want three new instructions: 61 | 62 | * complex multiplication 63 | * complex addition 64 | * complex absolute value (squared) 65 | 66 | These will fit into the same instruction type used by the the ALU operations (add, sub, xor etc). 67 | All three have one destination register, multiplication and addition have two source registers whereas absolute value only has one. 68 | 69 | ### Choosing a RISC-V instruction encoding 70 | 71 | We won't be getting into full encoding details in this lab, but just covering the ones we need (please consult the RISC-V ISA manual volume 1 if you want more details). 72 | The bottom 7 bits of a RISC-V instruction specify the 'major opcode' (in this table, the bottom two bits are always `2'b11`, so the table only shows bits 2:6): 73 | 74 | ![](./lab4_imgs/RISC-V_base_opcode_map.png) 75 | 76 | RISC-V reserves some major opcodes for custom instructions; we're going to use the *custom-0* opcode which has a value of `7'b0001011 = 7'h0b`. 77 | Let's call it `OPCODE_CMPLX`. 78 | 79 | All of our instructions will be *R-type* instructions, which have the following layout: 80 | 81 | ![](./lab4_imgs/R-type_instruction_encoding.png) 82 | 83 | The R-type instructions provide two source registers and one destination register. 84 | We'll use the *funct3* field to select which of our operations to execute: 85 | 86 | - `3'b000` - Complex Multiplication 87 | - `3'b001` - Complex Addition 88 | - `3'b010` - Complex Absolute Value (Squared) 89 | 90 | *funct7* will be set to 0 in all cases. 91 | For the absolute value operation, the *rs2* source register will always be `x0` (and ignored by the instruction). 92 | 93 | ## Adding custom instructions to Ibex 94 | 95 | Ibex has no explicit custom instruction support, however one can easily alter the RTL to add some. There are three source files in which we will need to make modifications. 96 | 97 | - `ibex_pkg.sv`: Constants and definitions 98 | - `ibex_decoder.sv`: Instruction decoder 99 | - `ibex_alu.sv`: Arithmetic-logical unit (ALU) 100 | 101 | ### Modifying `ibex_pkg.sv` 102 | 103 | Open the file `vendor/lowrisc_ibex/rtl/ibex_pkg.sv`, and make the following changes: 104 | 105 | 1. Add our new opcode (`OPCODE_CMPLX = 7'h0b`) to the opcode enum `opcode_e`. 106 | 2. Add three new operations, one for each new instruction, to the ALU operations enum `alu_op_e`. 107 | This is what is produced by the decoder to tell the ALU what to do. 108 | Name them whatever you think is best. 109 | 110 | ### Modifying `ibex_decoder.sv` 111 | 112 | Open `vendor/lowrisc_ibex/rtl/ibex_decoder.sv` and take a look. 113 | The first thing to note is the decoder is split into two. One part specifies things like register read and write enables, and the other part specifies ALU related signals. 114 | The decoder also has two copies of the instruction in seperate flops, which allows us to meet the timing constraints more easily. With a single set of flops, the 'fan-out' of those flops becomes very large and requires significant buffering (drive-strength), slowing the logic down. With the duplicate flops and split, the decoder fan-out is reduced, which improves performance. 115 | Tools can do this kind of duplication automatically, but it may not be enabled in all flows (in particular in ASIC synthesis), and tools may choose a split that doesn't work as well. Hence we explicity split the logic ourselves to guarantee the behaviour we want. 116 | 117 | Support for our custom opcode `OPCODE_CMPLX` needs to be added to both decoders: 118 | 119 | 1. The first decoder begins with `unique case (opcode)`. 120 | For `OPCODE_CMPLX` we must set the following signals: 121 | - `rf_ren_a_o`/`rf_ren_b_o`: Register file read enables 122 | - `rf_we`: Register file write enable 123 | - `illegal_insn`: set 1 if the instruction is illegal (e.g. *funct3* isn't one of the 3 values we are using) 124 | 2. The second decoder controls the ALU operation and begins with `unique case (opcode_alu)`. 125 | We must set the following signals: 126 | - `alu_op_a_mux_sel_o`: Mux select for ALU operand A, always set to `OP_A_REG_A` as we always read our operands from registers for our new instructions. 127 | - `alu_op_b_mux_sel_o`: Mux select for ALU operand B, always set to `OP_B_REG_B` as we always read our operands from registers for our new instructions. 128 | - `alu_operator_o`: The ALU operation, set it to one of the new values you created in `ibex_pkg.sv` depending upon the *funct3* field of the instruction. 129 | 130 | ### Modifying `ibex_alu.sv` 131 | 132 | Finally open the file `vendor/lowrisc_ibex/rtl/ibex_alu.sv` and take a look. 133 | The ALU takes in two operands (inputs `operand_a_i` and `operand_b_i`) along with an operator (input `operator_i`) to produce a result (output `result_o`). 134 | There are multiple parts to the ALU to handle different kinds of operations (the bit-manipulation extension for example adds significant complexity here). 135 | At the bottom of the file you will find the result multiplexer (mux), which outputs the result from the appropriate part of the ALU based upon the operator. 136 | 137 | We need to add new logic to implement our complex fixed point operations, but first let's look at fixed point representation and clamping/truncating behaviour. 138 | 139 | #### Fixed point representation 140 | 141 | Fixed point representation simply stores and operates on a scaled version of the true value. The true number is always multiplied by some constant, which is a power of two, so we can recover it by applying the reverse, a division by the constant. For the representation we're using, we choose that multiplier to be 4096 or $2^12$, so 1.0 would be represented by 4096, 2.0 by 8192, and 1.5 by 6144. This may seem slightly odd, but it allows us to make use of common ALU hardware extremely efficiently. 142 | 143 | Manipulating fixed point numbers is as straightforward as integers: 144 | For addition and subtraction simply perform the operations as normal: 145 | 146 | $$xF + yF = (x + y)F$$ 147 | 148 | where $F$ is our fixed multiplier 4096. 149 | For multiplication first multiply, then divide by the fixed constant: 150 | 151 | $$xF yF = xyF^2$$ 152 | 153 | ...so we need to divide by $F$ to get the $xyF$ result we want. 154 | 155 | As the constant $F$ is a power of two, the division is a bitshift, or in practice is just choosing which bits out of your multiplier are fed into the final result. 156 | 157 | #### Clamping/Truncation 158 | 159 | Before we continue, there is an important issue with multiplication, and to a lesser extent addition. 160 | Multiplying two 16-bit numbers can give you a 32-bit result (consider `0xffff` multiplied by `0xffff`). 161 | We drop the bottom 12 bits of this result to give us our constant divide, but that still leaves us with 20 bits of result and only 16 bits to place it in. 162 | There are two options: 163 | 164 | * Truncation: drop the top four bits; 165 | * Clamping: clamp the result to the maximum or minimum value as appropriate. 166 | 167 | For complex multiplication we'll use truncation, for two main reasons: 168 | Firstly, neither truncation nor clamping produce sensible overall results. The result of complex multiplication is the sum of multiple real multiplications, so clamping individual multiplications does not necessarily result in a sensible result overall (e.g., they could have different signs and cancel each other out). Truncation also produces a non-sensical overall result, but it's cheaper to implement and matches what normal multiplications do (e.g., in C if we do a 32-bit multiplication, we get a truncated 32-bit result). 169 | Secondly, we can just move the responsibility for providing sane inputs to the application, by ensuring that any multiplications remain within range (which is the case for our Mandelbrot set application). 170 | 171 | For the complex absolute value we'll use clamping, again for two main reasons: 172 | Firstly, squaring means no negative results, so clamping is sensible. If one of the multiplications maxes out, we'll get a large result back because the other multiplication can't produce some large negative that could cancel it out. 173 | Secondly, our Mandelbrot set application uses the absolute value to break multiplication iterations when exceeding a threshold, thereby keeping multiplications within range. 174 | This would not be possible if we used truncation in calculating the absolute value. 175 | 176 | Addition suffers from the same issue in that an extra bit (representing the carry-out) is added, so our 16-bit adds have 17 bits of result. 177 | For complex additions and for the additions to implement the complex multiplication, we will use truncation, with the same reasoning as for complex multiplication. 178 | For the addition at the end of the complex absolute value, we use the full 17-bit value of the sum and sign-extend it to 32 bits. 179 | 180 | #### Implementing the complex operations 181 | 182 | It's time to implement our new logic. 183 | Here's an outline for how you may want to implement it: 184 | 185 | ```systemverilog 186 | logic [15:0] rs1_real, rs1_imag; 187 | logic [15:0] rs2_real, rs2_imag; 188 | 189 | assign rs1_real = operand_a_i[31:16]; 190 | assign rs1_imag = operand_a_i[15:0]; 191 | assign rs2_real = operand_b_i[31:16]; 192 | assign rs2_imag = operand_b_i[15:0]; 193 | 194 | logic [15:0] mul1_res; 195 | 196 | // Multipliers for complex multiplication 197 | fp_mul#(.CLAMP(0)) mul1(.a_i(rs1_real), .b_i(rs2_real), .result_o(mul1_res)); 198 | // TODO: Add more multipliers here 199 | 200 | logic [15:0] real_sq_res; 201 | 202 | // Multpliers for complex absolute value 203 | fp_mul#(.CLAMP(1)) real_sq(.a_i(rs1_real), .b_i(rs1_real), .result_o(real_sq_res)); 204 | // TODO: Add another multiplier here 205 | 206 | logic [31:0] cmplx_result; 207 | 208 | always_comb begin 209 | cmplx_result = '0; 210 | 211 | unique case (operator_i) 212 | ALU_CMPLX_MUL: begin 213 | // TODO Implement this write result to `cmplx_result` 214 | end 215 | ALU_CMPLX_ADD: begin 216 | // TODO Implement this write result to `cmplx_result` 217 | end 218 | ALU_CMPLX_ABS_SQ: begin 219 | // TODO Implement this write result to `cmplx_result` 220 | end 221 | default: ; 222 | endcase 223 | end 224 | ``` 225 | 226 | The `fp_mul` module can be found in the supplementary material that accompanies this lab. 227 | Copy the `fp_mul.sv` file to `vendor/lowrisc_ibex/rtl` in the demo system repository and add it to the list of Ibex RTL files in `vendor/lowrisc_ibex/ibex_core.core`. 228 | 229 | Finally you will need to modify the ALU result mux to produce the result of the complex operation when one of the complex operands is selected. 230 | 231 | ## Testing our implementation 232 | 233 | With our new instructions implemented, how do we test them? 234 | We could just switch out the functions that do complex number manipulation in our Mandelbrot demo to use our new instructions, but if it doesn't work first time, it'll be hard to debug. 235 | So instead we will use a dedicated program to test the instructions against existing software implementations. 236 | You can find this in the `lab4_supplementary_material/cmplx_test` directory. 237 | Follow these steps to build it: 238 | 239 | 1. Copy the `cmplx_test` directory into the `sw/demo/` directory in the demo system repository 240 | 2. Add `add_subdirectory(cmplx_test)` on its own line in `sw/demo/CMakeLists.txt` 241 | 3. Switch to the `sw/build` directory (or whatever build directory you chose to use) and run `cmake ../ -DSIM_CTRL_OUTPUT=On` 242 | 4. Build the software with `make` 243 | 244 | Note the `-DSIM_CTRL_OUTPUT=On`. 245 | This redirects output from the UART to the simulator control which writes them to a log file. 246 | With this enabled you can see output from the program (in particular the test results) when running it through Verilator. 247 | Note that you need to rerun the `cmake` command with that option set to `Off` and rebuild the software to use it on FPGA. 248 | 249 | Run the `cmplx_test` binary you just built (path `sw/build/demo/cmplx_test/cmplx_test`). 250 | Look at the `ibex_demo_system.log` file that will be in the same directory you ran the simulation from. 251 | If you got your implementation correct, you will see "All tests passed". 252 | Otherwise a failure will be reported, and you will be told what test failed and given the input and output of the software version and what your instruction implementation did. It's time to open GTKWave and start debugging! 253 | 254 | ## Implementing Mandelbrot with our custom instructions 255 | 256 | Make a copy of the `fractal_fixed.c` file in `sw/demo/lcd_st7735`, e.g. calling it `fractal_cmplx_insn.c`. 257 | Modify `fractal_cmplx_insn.c` to use the new custom instructions. 258 | You can find functions that use the custom instructions in `cmplx_test/main.c`. 259 | We suggest just copying these functions into your `fractal_cmplx_insn.c` and using those rather than taking the inline assembly within them and using that directly. 260 | Don't forget to add `fractal_cmplx_insn.c` to the list of source files for `lcd_st7735` in `sw/demo/lcd_st7735/CMakeLists.txt`. 261 | 262 | A few modifications will be needed to switch to using the custom instructions: 263 | 264 | * Remove all the fixed point and complex number functions (though you'll want to keep the macros) 265 | * Write a function that can take a `cmplx_fixed_t` and pack it into a 32-bit integer for use with the custom instructions. 266 | * Alter `mandel_iters_fixed` to use the complex number instructions. 267 | * Rename `mandel_iters_fixed` e.g. to `mandel_iters_cmplx_insn` 268 | * Update `fractal_mandelbot_fixed` similarly. 269 | * Call your new mandelbrot function from `main.c` as part of the fractal drawing, giving a final result that first uses the floating point implementation, then the fixed point implementation, and finally the implementation with instructions. 270 | 271 | ## Further Exercises / Questions 272 | 273 | 1. Our new instructions add several new multipliers to Ibex, can we reduce this by sharing them? 274 | Can we share with the existing multipliers (it currently has 3 16x16 ones to implement the single-cycle multiplication)? 275 | Is there any point in doing this on an FPGA where DSPs are fixed function so our multiplier usage isn't consuming general logic? 276 | Would it be more worthwhile on ASIC? 277 | 2. Our use of a major opcode for our custom instructions is simple but very wasteful of encoding space. 278 | Could we use an existing major opcode (e.g. `OPCODE_OP` which the bitmanip extension uses along with the base RV32I instructions)? 279 | 3. What other kinds of clamping/truncating behaviour would make sense for our complex number implementation? 280 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public 379 | licenses. Notwithstanding, Creative Commons may elect to apply one of 380 | its public licenses to material it publishes and in those instances 381 | will be considered the “Licensor.” The text of the Creative Commons 382 | public licenses is dedicated to the public domain under the CC0 Public 383 | Domain Dedication. Except for the limited purpose of indicating that 384 | material is shared under a Creative Commons public license or as 385 | otherwise permitted by the Creative Commons policies published at 386 | creativecommons.org/policies, Creative Commons does not authorize the 387 | use of the trademark "Creative Commons" or any other trademark or logo 388 | of Creative Commons without its prior written consent including, 389 | without limitation, in connection with any unauthorized modifications 390 | to any of its public licenses or any other arrangements, 391 | understandings, or agreements concerning use of licensed material. For 392 | the avoidance of doubt, this paragraph does not form part of the 393 | public licenses. 394 | 395 | Creative Commons may be contacted at creativecommons.org. 396 | 397 | --------------------------------------------------------------------------------