diff --git a/FemtoRV/FIRMWARE/EXAMPLES/hello_LED.c b/FemtoRV/FIRMWARE/EXAMPLES/hello_LED.c index d4a6e5d1..377e1c66 100644 --- a/FemtoRV/FIRMWARE/EXAMPLES/hello_LED.c +++ b/FemtoRV/FIRMWARE/EXAMPLES/hello_LED.c @@ -6,12 +6,7 @@ int main() { MAX7219_tty_init(); // redirect printf() to led matrix scroller for(;;) { -// printf("Hello, RISC-V world \001 \002 \001 \002 "); -// printf("Hello, TelecomNancy ! \001 \002 Best school ! \001 \002 "); -// printf("Hello FemtoRV friend !!! \001 \002 \001 \002 "); -// printf("Hello, Hackaday \001 \002 Greetings from FemtoRV !!! "); -// printf("Hello, RISC-V world \001 \002 \001 \002 "); - printf("Hello, caf\202 LoOPS ! \001 \002 \001 \002 "); + printf("Hello, RISC-V world \001 \002 \001 \002 "); } return 0; } diff --git a/FemtoRV/FIRMWARE/EXAMPLES/pi.c b/FemtoRV/FIRMWARE/EXAMPLES/pi.c index ea3e63b3..95e75abb 100644 --- a/FemtoRV/FIRMWARE/EXAMPLES/pi.c +++ b/FemtoRV/FIRMWARE/EXAMPLES/pi.c @@ -176,8 +176,8 @@ int digits(int n) { int main() { MAX7219_tty_init(); // Uncomment to display on led matrix. -// femtosoc_tty_init(); -// GL_set_font(&Font3x5); + femtosoc_tty_init(); + GL_set_font(&Font3x5); // GL_set_font(&Font8x16); printf("pi = 3."); for(int n=1; ;n+=9) { diff --git a/FemtoRV/TUTORIALS/FPU.md b/FemtoRV/TUTORIALS/FPU.md index ed016724..da026175 100644 --- a/FemtoRV/TUTORIALS/FPU.md +++ b/FemtoRV/TUTORIALS/FPU.md @@ -656,3 +656,6 @@ References - [A FPU written in system verilog](https://github.com/taneroksuz/riscv-fpu) - [Berkeley SoftFloat and HardFloat](http://www.jhauser.us/arithmetic/) + +- [T9000 transputer FPU design](https://transputer.net/fbooks/t9000/t9kfpdsn.pdf) +- [Transputer](https://thechipletter.substack.com/p/inmos-and-the-transputer-instruction) diff --git a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/GL_tty.h b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/GL_tty.h new file mode 100644 index 00000000..a61932c0 --- /dev/null +++ b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/GL_tty.h @@ -0,0 +1,460 @@ +/** + * ansi_graphics.h + * A couple of function to display graphics in the terminal, + * using ansi sequences. + * Bruno Levy, Jan 2024 + */ + +#include +#include +#include +#include + +#ifndef GL_FPS +#define GL_FPS 30 +#endif + +#if defined(__linux__) || defined(_WIN32) || defined(__APPLE__) +#define BIGCPU // we are compiling for a real machine +#else +#define TINYCPU // we are compiling for a softwore +#endif + +#ifdef __linux__ +#include // for usleep() +#endif + +// You can define GL_width and GL_height before +// #including ansi_graphics.h in case the plain +// old 80x25 pixels does not suffice. + +#ifndef GL_width +#define GL_width 80 +#endif + +#ifndef GL_height +#define GL_height 25 +#endif + +/** + * \brief Sets the current graphics position + * \param[in] x typically in 0,79 + * \param[in] y typically in 0,24 + */ +static inline void GL_gotoxy(int x, int y) { + printf("\033[%d;%dH",y,x); +} + +/** + * \brief Sets the current graphics position + * \param[in] R , G , B the RGB color of the pixel, in [0..255] + * \details Typically used by programs that draw all pixels sequentially, + * like a raytracer. After each line, one can either printf("\n") or + * call GL_gotoxy(). If you want to draw individual pixels in an + * arbitrary order, use GL_setpixelRGB(x,y,R,G,B) + */ +static inline void GL_setpixelRGBhere(uint8_t R, uint8_t G, uint8_t B) { + // set background color, print space + printf("\033[48;2;%d;%d;%dm ",(int)R,(int)G,(int)B); +} + + +/** + * \brief Draws two "pixels" at the current + * cursor position and advances the current cursor + * position. + * \details Characters are roughly twice as high as wide. + * To generate square pixels, this function draws two pixels in + * the same character, using the special lower-half white / upper-half + * black character, and setting the background and foreground colors. + */ +static inline void GL_set2pixelsRGBhere( + uint8_t r1, uint8_t g1, uint8_t b1, + uint8_t r2, uint8_t g2, uint8_t b2 +) { + if((r2 == r1) && (g2 == g1) && (b2 == b1)) { + GL_setpixelRGBhere(r1,g1,b1); + } else { + printf("\033[48;2;%d;%d;%dm",(int)r1,(int)g1,(int)b1); + printf("\033[38;2;%d;%d;%dm",(int)r2,(int)g2,(int)b2); + // https://www.w3.org/TR/xml-entity-names/025.html + // https://onlineunicodetools.com/convert-unicode-to-utf8 + // https://copypastecharacter.com/ + printf("\xE2\x96\x83"); + } +} + +#define GL_RGB(R,G,B) #R ";" #G ";" #B + +static inline void GL_setpixelIhere( + const char** cmap, int c +) { + // set background color, print space + printf("\033[48;2;%sm ",cmap[c]); +} + +static inline void GL_set2pixelsIhere( + const char** cmap, int c1, int c2 +) { + if(c1 == c2) { + GL_setpixelIhere(cmap, c1); + } else { + printf("\033[48;2;%sm",cmap[c1]); + printf("\033[38;2;%sm",cmap[c2]); + // https://www.w3.org/TR/xml-entity-names/025.html + // https://onlineunicodetools.com/convert-unicode-to-utf8 + // https://copypastecharacter.com/ + printf("\xE2\x96\x83"); + } +} + +/** + * \brief Moves the cursor position to the next line. + * \details Background and foreground colors are set to black. + */ +static inline void GL_newline() { + printf("\033[38;2;0;0;0m"); + printf("\033[48;2;0;0;0m\n"); +} + +/** + * \brief Sets the color of a pixel + * \param[in] x typically in 0,79 + * \param[in] y typically in 0,24 + * \param[in] R , G , B the RGB color of the pixel, in [0..255] + */ +static inline void GL_setpixelRGB( + int x, int y, uint8_t R, uint8_t G, uint8_t B +) { + GL_gotoxy(x,y); + GL_setpixelRGBhere(R,G,B); +} + +/** + * \brief restore default foreground and background colors + */ +static inline void GL_restore_default_colors() { + printf( + "\033[48;5;16m" // set background color black + "\033[38;5;15m" // set foreground color white + ); +} + +/** + * \brief Call this function each time graphics should be cleared + */ +static inline void GL_clear() { + GL_restore_default_colors(); + printf("\033[2J"); // clear screen +} + +/** + * \brief Moves current drawing position to top-left corner + * \see GL_setpixelRGBhere() and GL_set2pixelsRGBhere() + */ +static inline void GL_home() { + printf("\033[H"); +} + +/** + * \brief Call this function before starting drawing graphics + * or each time graphics should be cleared + */ +static inline void GL_init() { + printf("\033[?25l"); // hide cursor + GL_home(); + GL_clear(); +} + + +/** + * \brief Call this function at the end of the program + */ +static inline void GL_terminate() { + GL_restore_default_colors(); + GL_gotoxy(0,GL_height); + printf("\033[?25h"); // show cursor +} + +/** + * \brief Flushes pending graphic operations and waits a bit + */ +static inline void GL_swapbuffers() { + // only flush if we are on a big machine, with true stdio support + // otherwise does nothing (because our small MCU io lib is not buffered) +#ifdef BIGCPU + fflush(stdout); +#endif +#ifdef __linux__ + usleep(1000000/GL_FPS); +#endif +} + +typedef void (*GL_pixelfunc_RGB)(int x, int y, uint8_t* r, uint8_t* g, uint8_t* b); +typedef void (*GL_pixelfunc_RGBf)(int x, int y, float* r, float* g, float* b); + +/** + * \brief Draws an image by calling a user-specified function for each pixel. + * \param[in] width , height dimension of the image in square pixels + * \param[in] do_pixel the user function to be called for each pixel + * (a "shader"), that determines the (integer) components r,g,b of + * the pixel's color. + * \details Uses half-charater pixels. + */ +static inline void GL_scan_RGB( + int width, int height, GL_pixelfunc_RGB do_pixel +) { + uint8_t r1, g1, b1; + uint8_t r2, g2, b2; + GL_home(); + for (int j = 0; j 1.0f) ? 1.0f : f; + return (uint8_t)(255.0f * f); +} + +/** + * \brief Draws an image by calling a user-specified function for each pixel. + * \param[in] width , height dimension of the image in square pixels + * \param[in] do_pixel the user function to be called for each pixel + * (a "shader"), that determines the (floating-point) components + * fr,fg,fb of the pixel's color. + * \details Uses half-charater pixels. + */ +static inline void GL_scan_RGBf( + int width, int height, GL_pixelfunc_RGBf do_pixel +) { + float fr1, fg1, fb1; + float fr2, fg2, fb2; + uint8_t r1, g1, b1; + uint8_t r2, g2, b2; + GL_home(); + for (int j = 0; j XMAX)<<1) | (((y) < YMIN)<<2) | (((y) > YMAX)<<3) + +/***************************************************************/ + +static inline void GL_line( + int x1, int y1, int x2, int y2, int R, int G, int B +) { + int x,y,dx,dy,sx,sy,tmp; + + /* Cohen-Sutherland line clipping. */ + int code1 = code(x1,y1); + int code2 = code(x2,y2); + int codeout; + + for(;;) { + /* Both points inside. */ + if(code1 == 0 && code2 == 0) { + break; + } + + /* No point inside. */ + if(code1 & code2) { + return; + } + + /* One of the points is outside. */ + codeout = code1 ? code1 : code2; + + /* Compute intersection. */ + if (codeout & TOP) { + x = x1 + (x2 - x1) * (YMAX - y1) / (y2 - y1); + y = YMAX; + } else if (codeout & BOTTOM) { + x = x1 + (x2 - x1) * (YMIN - y1) / (y2 - y1); + y = YMIN; + } else if (codeout & RIGHT) { + y = y1 + (y2 - y1) * (XMAX - x1) / (x2 - x1); + x = XMAX; + } else if (codeout & LEFT) { + y = y1 + (y2 - y1) * (XMIN - x1) / (x2 - x1); + x = XMIN; + } + + /* Replace outside point with intersection. */ + if (codeout == code1) { + x1 = x; + y1 = y; + code1 = code(x1,y1); + } else { + x2 = x; + y2 = y; + code2 = code(x2,y2); + } + } + + // Swap both extremities to ensure x increases + if(x2 < x1) { + tmp = x2; + x2 = x1; + x1 = tmp; + tmp = y2; + y2 = y1; + y1 = tmp; + } + + // Bresenham line drawing. + dy = y2 - y1; + sy = 1; + if(dy < 0) { + sy = -1; + dy = -dy; + } + + dx = x2 - x1; + + x = x1; + y = y1; + + if(dy > dx) { + int ex = (dx << 1) - dy; + for(int u=0; u= 0) { + x++; + ex -= dy << 1; + GL_setpixelRGB(x,y,R,G,B); + } + while(ex >= 0) { + x++; + ex -= dy << 1; + putchar(' '); + } + ex += dx << 1; + } + } else { + int ey = (dy << 1) - dx; + for(int u=0; u= 0) { + y += sy; + ey -= dx << 1; + GL_setpixelRGB(x,y,R,G,B); + } + ey += dy << 1; + } + } +} + + +/***************************************************************/ + +#ifdef GL_USE_TURTLE + +#include "sintab.h" // Ugly !!! + +typedef struct { + int x; // in [0..79] + int y; // in [0..24] + int angle; // in degrees + int R,G,B; // pen color + int pendown; // draw if non-zero +} Turtle; + +static inline void Turtle_init(Turtle* T) { + T->x = GL_width/2; + T->y = GL_height/2; + T->angle = -90; + T->pendown = 1; + T->R = 255; + T->G = 255; + T->B = 255; +} + +static inline void Turtle_pen_up(Turtle* T) { + T->pendown = 0; +} + +static inline void Turtle_pen_down(Turtle* T) { + T->pendown = 1; +} + +static inline void Turtle_pen_color(Turtle* T, int R, int G, int B) { + T->R = R; + T->G = G; + T->B = B; +} + +static inline void Turtle_forward(Turtle* T, int distance) { + int last_x = T->x; + int last_y = T->y; + int a = T->angle; + while(a < 0) { + a += 360; + } + while(a > 360) { + a -= 360; + } + T->x += (costab[a] * distance) / 256; + T->y += (sintab[a] * distance) / 256; + if(T->pendown) { + GL_line(last_x, last_y, T->x, T->y, T->R, T->G, T->B); + } +} + +static inline void Turtle_backward(Turtle* T, int distance) { + Turtle_forward(T,-distance); +} + +static inline void Turtle_turn_right(Turtle* T, int delta_angle) { + T->angle += delta_angle; +} + +static inline void Turtle_turn_left(Turtle* T, int delta_angle) { + Turtle_turn_right(T, -delta_angle); +} + +#endif diff --git a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/humanshader.c b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/humanshader.c new file mode 100644 index 00000000..72a419ce --- /dev/null +++ b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/humanshader.c @@ -0,0 +1,113 @@ +// C version of humanshader +// See https://humanshader.com/ +// (using a computer is clearly not as fun, but it is interesting to have +// a small not too computationally expensive raytracing program that +// can run on small softcores for PGAs). +// Using the 16-bits version with no divide from here: https://www.shadertoy.com/view/XflXDs + +#define GL_width 71 +#define GL_height 40 +#include "GL_tty.h" + +void human_shader( + int x, int y, uint8_t* r_out, uint8_t* g_out, uint8_t* b_out +) { + int R, B; + + //------------------------- + // Section A (2 MUL, 3 ADD) + //------------------------- + int u = x-36; + int v = 18-y; + int u2 = u*u; + int v2 = v*v; + int h = u2 + v2; + //------------------------- + + if( h < 200 ) + { + //------------------------------------- + // Section B, Sphere (4/7 MUL, 5/9 ADD) + //------------------------------------- + R = 420; + B = 520; + + int t = 5200 + (h<<3); + int p = (t*u)>>7; + int q = (t*v)>>7; + + // bounce light + int w = 18 + (((p*5-q*13))>>9); + if( w>0 ) R += w*w; + + // sky light / ambient occlusion + int o = q + 900; + R = (R*o)>>12; + B = (B*o)>>12; + + // sun/key light + if( p > -q ) + { + int w = (p+q)>>3; + R += w; + B += w; + } + //------------------------- + } + else if( v<0 ) + { + //------------------------------------- + // Section C, Ground (5/9 MUL, 6/9 ADD) + //------------------------------------- + R = 150 + (v<<1); + B = 50; + + int p = h + (v2<<3); + int c = 240*(-v) - p; + + // sky light / ambient occlusion + if( c>1200 ) + { + int o = (25*c)>>3; + o = (c*(7840-o)>>9) - 8560; + R = (R*o)>>10; + B = (B*o)>>10; + } + + // sun/key light with soft shadow + int r = c + u*v; + int d = 3200 - h - (r<<1); + if( d>0 ) R += d; + //------------------------- + } + else + { + //------------------------------ + // Section D, Sky (1 MUL, 2 ADD) + //------------------------------ + int c = x + (y<<2); + R = 132 + c; + B = 192 + c; + //------------------------- + } + + //------------------------- + // Section E (3 MUL, 1 ADD) + //------------------------- + if(R > 255) R = 255; + if(B > 255) B = 255; + + int G = (R*11 + 5*B)>>4; + //------------------------- + + *r_out = (uint8_t)R; + *g_out = (uint8_t)G; + *b_out = (uint8_t)B; +} + +int main() { + GL_init(); + GL_scan_RGB(GL_width, GL_height, human_shader); + GL_terminate(); + return 0; +} diff --git a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/pi.c b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/pi.c index 80e19de1..dd17a91a 100644 --- a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/pi.c +++ b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/FIRMWARE/pi.c @@ -88,8 +88,9 @@ int is_prime(int n) if ((n % 2) == 0) return 0; - r = (int) (sqrt(n)); - for (i = 3; i <= r; i += 2) + //r = (int) (sqrt(n)); + //for (i = 3; i <= r; i += 2) + for (i = 3; i*i <= n; i += 2) if ((n % i) == 0) return 0; return 1; diff --git a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/INTERRUPTS.md b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/INTERRUPTS.md index de079d1d..c9cf7cff 100644 --- a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/INTERRUPTS.md +++ b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/INTERRUPTS.md @@ -284,3 +284,4 @@ Links: - @MrBossman [kisc-v](https://github.com/Mr-Bossman/KISC-V) - @splinedrive [Kian risc-V](https://github.com/splinedrive/kianRiscV) + diff --git a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/README.md b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/README.md index a18f26e3..d3f80b30 100644 --- a/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/README.md +++ b/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/README.md @@ -719,7 +719,7 @@ The register bank is implemented as follows: ``` Let us take a closer look at what we need to to to execute an instruction. -Condider for instance a stream of R-type instructions. For each instruction, +Consider for instance a stream of R-type instructions. For each instruction, we need to do the following four things: - fetch the instruction: `instr <= MEM[PC]` diff --git a/FemtoRV/TUTORIALS/Morph.md b/FemtoRV/TUTORIALS/Morph.md new file mode 100644 index 00000000..5eb7730e --- /dev/null +++ b/FemtoRV/TUTORIALS/Morph.md @@ -0,0 +1,51 @@ +FemtoRV-Morph - Notes +===================== + +Mission statement: create a Risc-V processor with an _interesting_ +capabilities/complexity/performance ratio. The main idea is to start from +an RV32ICZicsr core like Gracilis, add the instructions that have an +interesting area/performance ratio (e.g., MUL variants and a subset of RV32F) +and implement the rest in software traps. There would be two versions, +Caterpillar and QuickSilver (abbreviated Hg). + +FemtoRVMorph-Caterpillar +======================== + +- RV32ICZicsr (Gracilis base) + +- RV32M: MUL, MULH, MULHSU, MULHU (DIV, DIVU, REM, REMU in sw trap) + +- Implement a subset of RV32F (separate fp registers) or Zfinx (shared registers) + Zfinx may be the way to go (much simpler datapath, smaller nb instructions), + and Zfinx could be used to emulate RV32F in trap ! + + It is mostly a FMA (Fused-Multiply-Add) unit, used to implement: + - Sum and product: FADD, FSUB, FMUL, FMADD, FMSUB, FNMADD, FNMSUB + - Comparison: FEQ, FLT, FLE + - Load/Store (RV32F): FLW, FSW + - integer reg <-> fp reg (RV32F): FMVXW, FMVWX + - All the rest in sw trap + +- Questions: + - RV32F or Zfinx ? + - single-precision or double-precision ? + +FemtoRVMorph-Hg (QuickSilver) +============================= + +- Pipelined with branch prediction +- I$, D$ caches +- MCT (Minimal Cost Trap) mechanism: cooperation between illegal instruction + trap mechanism and address prediction logic, fast context switch logic +- Pipelined FMA unit +- Some sort of Conway/Scoreboard/Tomasulo dynamic execution mechanism to make + best use of pipelined FMA unit + +- Questions: + - RV32F or Zfinx ? Probably start with Zfinx (one difficulty at a time: + more complicated RV32F datapath will be harder with pipeline), then + "morph" it to RV32F + - single-precision or double-precision ? Depends on FPGA capabilities. + - other instructions in hw: + - vector math + - extensions implemented by Hazard3 (huge performance gain it seems) diff --git a/README.md b/README.md index 4549516f..9e0d52db 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,24 @@ functional RISC-V core that can compute and display graphics. In [Episode II](https://github.com/BrunoLevy/learn-fpga/blob/master/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/PIPELINE.md), you will learn how to design a pipelined processor. +Links - Other FPGA resources +---------------------------- +- [TinyPrograms](https://github.com/BrunoLevy/TinyPrograms) Tiny yet interesting C programs to play with your softcore +- [LiteX](https://github.com/enjoy-digital/litex) Framework in Amaranth (Python-based HDL) to build SOCs +- [Silice](https://github.com/sylefeb/Silice) A new HDL by my friend Sylvain Lefebvre +- [FuseSOC](https://github.com/olofk/fusesoc) and [Edalize](https://github.com/olofk/edalize), package manager and abstraction of FPGA tools +- [PipelineC](https://github.com/JulianKemmerer/PipelineC) Transform a C program into a pipelined specialized core ! +- [ultraembedded](https://github.com/ultraembedded/) Amazing resources, [FatIOLib](https://github.com/ultraembedded/fat_io_lib),[ExactStep](https://github.com/ultraembedded/exactstep)... +- [picoRV](https://github.com/YosysHQ/picorv32) by Claire Wolf, my principal source of inspiration +- [VexRiscV](https://github.com/SpinalHDL/VexRiscv) and [NaxRiscV](https://github.com/SpinalHDL/NaxRiscv), performant and configurable pipelined and OoO cores, by Charles Papon, in SpinalHDL +- [SERV](https://github.com/olofk/serv) the tiniest RiscV core, with a bit-serial ALU +- [DarkRiscV](https://github.com/darklife/darkriscv) a simple pipelined core (written in one night according to the legend) +- [kianRiscV](https://github.com/splinedrive/kianRiscV) a simple yet complete Linux-capable core + soc +- [TinySys](https://github.com/ecilasun/tinysys/wiki) not that tiny SOC and OS +- [Will Green's project F](https://github.com/projf/projf-explore) tutorials with nice graphics effects +- [fpga4fun](https://www.fpga4fun.com/) learned there how to create VGA graphics +- [CoreScore](https://corescore.store/) how many cores can you fit on a FPGA ? + Basic: more basic things I wrote during May 2020 - June 2020 ------------------------------------------------------------ Files are [here](https://github.com/BrunoLevy/learn-fpga/tree/master/Basic).