ãæšæ¶ïŒ
ååã DMAãFPGAã«åŒãäžããããšã«æ±ºããŸããã
ä»æ¥ãFPGAã«ããªããã£ãLCDã³ã³ãããŒã©ãŒãå®è£ ãããã®ã³ã³ãããŒã©ãŒã§åäœãããã¬ãŒã ãããã¡ãŒãã©ã€ããŒãäœæããŸãã
FPGAåãã®éçºãšLinuxåããã©ã€ããŒã®äœæãéåžžã«ç°¡åã§ãããèå³æ·±ãããšãããäžåºŠç¢ºèªããŸãã
ãŸããæåŸã«å°ããªäžè«èª¿æ»ããããŸã-ç§ã¯ã³ãã¥ããã£ã®æèŠãç¥ãããã§ãã é£ãããªããã°ãæ祚ããŠãã ããã
ãã®ããã Cyclone Väžã®HPSã«ã¯çµ±åã°ã©ãã£ãã¯ã¹ã³ã³ãããŒã©ãŒããããŸããã ãã£ã¹ãã¬ã€ãªãã§ã¯çããŠãããŸãã- 枬å®çµæã¯ã©ãã§å ¥æã§ããŸããïŒ
ãã¡ããããã¬ãŒã ãããã¡ã®ãœãããŠã§ã¢å®è£ ïŒdeferred_ioãšããã«ãããã¡ãªã³ã°ã®åœ¢ã§ããããçš®é¡ã®æçšæ§ãè¿œå ãããŠããïŒã䜿çšããããšã§ã®ã¿å¯Ÿå¿ã§ããŸãã ããããããã¯ãã¹ãŠåãã§ãããããã»ã©éãã¯ãªãã確ãã«ç§ãã¡ãéžãã ãã®ã»ã©èå³æ·±ããã®ã§ã¯ãããŸããã
ãããŠãFPGAã§éåžžã«ã·ã³ãã«ã§ãããªããå®å šã«æ©èœããLCDã³ã³ãããŒã©ãŒã®å®è£ ãéžæããŸããããã«ããããã£ã¹ãã¬ã€ã«ããŒã¿ãæžã蟌ããšãã圢ã§CPUããäžèŠãªè² è·ãåãé€ãããŸãã
ILI9341ãããã«åºã¥ããLCDã䜿çšããŸãã
ãããã£ãŠãä»æ¥ã®èšç»ïŒ
- 建ç¯ã«ã€ããŠèãã
- ç§ãã¡ã¯LCDãç 究ããŠããŸã
- Linuxãã©ã€ããŒã®äœæ
- FPGAã§ã¢ãžã¥ãŒã«ãéçºããŸã
- U-bootã§äœããæ§æãã
- ãããã°
建ç¯
Linux ãã¬ãŒã ãããã¡ãŒãšã¯äœã§ããïŒ
ç°¡åã«èšãã°ãããã¯åãªãã¡ã¢ãªé åã§ããããã£ã¹ãã¬ã€ã«èšé²ããããã£ã¹ãã¬ã€ã«ã€ãªããèšé²ã§ãã
ãŠãŒã¶ãŒã¹ããŒã¹ããã®ã¢ã¯ã»ã¹ã¯ãããã€ã¹ãã¡ã€ã«/ dev / fb [N]ãä»ããŠè¡ãããŸãã
éåžžãæšæºã·ã¹ãã ã³ãŒã«ãå®è£ ãããŠããŸã-openïŒïŒãcloseïŒïŒãreadïŒïŒãwriteïŒïŒãlseekïŒïŒãããã³mmapïŒïŒã
ãã©ã€ããŒã§ã¯ãã»ãšãã©ã®é¢æ°ã1ã€ã®ã¿ã¹ã¯ãå®è¡ããŸããããã¯ãã¡ã¢ãªã«ä¿åãããŠãããã¬ãŒã ãæŽæ°ããããšã§ãã
ããŒããŠã§ã¢ã«å®è£ ãããŠããªãå Žåãã¡ã¢ãªããLCDã«ããŒã¿ãã³ããŒããæ©èœããããŸãã
ãã¹ãŠã®æ§é ãšæ©èœã«ã€ããŠã¯ããããã®èšäºïŒ ãªã³ã¯1åãšãªã³ã¯2ïŒã§è©³ãã説æãããŠããŸãã
æ å ±ãè€è£œããŠãæå³ããªããããã¢ãŒããã¯ãã£ã«åœ±é¿ãäžãããã®ã®ã¿ãåæããŸãã
ãã®ãããã¡ã¢ãªå ã®ããŒã¿ãæŽæ°ããããã«èšèšãããããã€ãã®é¢æ°ããããŸãã 圌ãã¯ãããã«ç°ãªã眲åãæã£ãŠããŸãã ãããã®é¢æ°ã䜿çšãããšããã¬ãŒã å šäœãšæ°ãã¯ã»ã«ã®ã¿ãåæç»ã§ããŸãã
ãã¬ãŒã ãæŽæ°ããåŸãLCDã«åé¡ããå¿ èŠããããŸãã åœç¶ããã¬ãŒã ã®å€æŽãããéšåã®ã¿ãã³ããŒããã«ã¯ãããå°ãªãããŒã¿ã®è»¢éãå¿ èŠã§ãã ãŸããCPUã䜿çšããŠããŒã¿ãã³ããŒããå Žåããããèæ ®ããå¿ èŠããããŸãã
ãã ããã³ããŒã¯FPGAã®DMAã³ã³ãããŒã©ãŒã«ãã£ãŠå®è¡ããããããããã«ã€ããŠå¿é ããããšã¯ãªãããã¬ãŒã å šäœãåæç»ããŸãã
次ã®è³ªåã¯ããã€ãã¬ãŒã ãåæç»ãããã§ãã ç°¡åãªè§£æ±ºçã®1ã€ã¯ãåæçã«ãã€ãŸãã¡ã¢ãªå ã®ããŒã¿ãæŽæ°ããåé¢æ°ã®æåŸã«ã¬ã³ããªã³ã°ãå®è¡ããããšã§ãã ããã¯ãmmapïŒïŒã䜿çšããå Žåãé€ãããã¹ãŠã®å Žåã«æå¹ã§ãã
ãããã³ã°ãå®è¡ããåŸããŠãŒã¶ãŒç©ºéããã»ã¹ãã¡ã¢ãªã®å 容ãå€æŽããææãå€æããã®ã¯ããã»ã©ç°¡åã§ã¯ãããŸããã ãã®åé¡ã¯deferred_ioã§è§£æ±ºã§ããŸãïŒåæã«ãæŽæ°ããåæç»ãå¿ èŠãªç¹å®ã®ã¡ã¢ãªããŒãžãå®çŸ©ããŸãïŒã ããããå®è£ ã¯ã§ããã ãã·ã³ãã«ã§ç°¡åãªãã®ã«ããå¿ èŠããããããå¥ã®æ¹æ³ã§è¡ããŸãã
FPGAã®ã³ã³ãããŒã©ãŒã¯ãn FPSã®åšæ³¢æ°ã§ãã¬ãŒã å šäœãã¬ã³ããªã³ã°ããŸãã ãããŠããã©ã€ããŒé¢æ°ã§ã¡ã¢ãªãæŽæ°ããããšã«é¢ããŠããããéåæçã«è¡ããŸãã ãããã£ãŠããã©ã€ããŒã§è¡ãå¿ èŠãããã®ã¯ãLCDãšFPGAã³ã³ãããŒã©ãŒã®åæåã ãã§ãã ãããŠãå®è£ ããå¿ èŠã®ãªããã¬ãŒã ãããã¡ã¡ã¢ãªã«ããŒã¿ãæžã蟌ãã§ãããã®ããã®æšæºæ©èœããã§ã«ãããŸãã
FPGAã®ã³ã³ãããŒã©ãŒãéåžžã«ã·ã³ãã«ã§ãã 圌ã®ä»äºïŒ
- fpga2sdramãŸãã¯fpga2hpsã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããŠãæå®ãããé åããããŒã¿ãèªã¿åããŸã
- èªã¿åãããŒã¿ãLCDã«è»¢éããå¿ èŠãªãã©ã³ã¶ã¯ã·ã§ã³ã圢æããŸã
- CPUãLCDãä»ããŠã€ã³ã¿ãŒãã§ãŒã¹ã«çŽæ¥ã¢ã¯ã»ã¹ã§ããããã«ããŸã
- æå®ãããFPSãçºè¡ãã
LCDã®èª¬æ
LCDã«ã€ããŠç¥ãå¿ èŠãããã®ã¯ãLCDãåæåããæ¹æ³ãšãæžã蟌ã¿ãã©ã³ã¶ã¯ã·ã§ã³ãã©ã®ããã«èŠãããã ãã§ãã
ãã©ã€ããŒã«å°éãããšãã«åæåãæ€èšãããã©ã³ã¶ã¯ã·ã§ã³ã調ã¹ãŸãã
FPGAïŒããŒã¿è»¢éçšïŒãšãã©ã€ããŒïŒãã£ã¹ãã¬ã€èšå®çšïŒã®äž¡æ¹ã«å®è£ ããå¿ èŠããããŸãã
ILI9341ã¯è€æ°ã®ã€ã³ã¿ãŒãã§ã€ã¹ããµããŒãããŠããŸãã ç§ã¯8080ãšåŒã°ããIntelã®ããã»ããµãŒã®ååã§æåã«ç»å Žãã16ãããã®ãã©ã¬ã«ã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããŸãã ä¿¡å·ã¯æ¬¡ã®ãšããã§ãïŒæåã¯ããäžè¬çãªååã瀺ãããæ¬åŒ§å ã¯ILI9341ã®ããŒã¿ã·ãŒãããã®ååã§ãïŒã
- CSïŒCSXïŒ-ãããéžæãã¢ã¯ãã£ãã¬ãã«0ãããããéžæããããã®ä¿¡å·ã¯ãå°é¢ã«å·»ãããŠããŸãã
- RSTïŒRESXïŒ-ãªã»ãããã¢ã¯ãã£ãã¬ãã«0ããªã»ããä¿¡å·ãGPIO HPSã§ååŸããŸããã
- RSïŒD / CXïŒ-ã¬ãžã¹ã¿éžæã ä¿¡å·ã0ã®å Žåãã³ãã³ãã¯DATAãã¹ã§çºè¡ããããã以å€ã®å Žåã¯ããŒã¿ãçºè¡ãããŸãã
- WRïŒWRXïŒ-ã¹ããããæžã蟌ã¿ãŸãã ã¹ãããé²é³ã
- RDïŒRDXïŒ-èªã¿åãã¹ãããã èªæžã¹ãããã
- DATAïŒDïŒ-RSã«å¿ããããŒã¿ãŸãã¯ã³ãã³ãã
æžã蟌ã¿ãã©ã³ã¶ã¯ã·ã§ã³ã¯éåžžã«ç°¡åã§ãã
æžã蟌ã¿ãã©ã³ã¶ã¯ã·ã§ã³
èªã¿åããã©ã³ã¶ã¯ã·ã§ã³ã¯ããã»ã©è€éã§ã¯ãããŸããããå¿ èŠãªããããèæ ®ããŸããã
Linuxãã©ã€ããŒ
ãã©ã€ããŒã«ã¯äœããããŸããïŒ
ãŸããFPGAã¬ãžã¹ã¿ã®èªã¿åã/æžã蟌ã¿çšã®é¢æ°ã ç§ã®ååã§ããishevchukã® èšäºã§ãã¹ããŒã¿ã¹å¶åŸ¡ã¬ãžã¹ã¿ãšã¯äœãããããŠãããã䜿çšããæ¹æ³ã«ã€ããŠè©³ããèªãããšãã§ããŸãã
CSRã®èªã¿åã/æžã蟌ã¿æ©èœ
static void fpga_write_reg(int reg, u16 val) { iowrite16(val, fpga_regs + 2*reg); } static u16 fpga_read_reg(int reg) { u16 tmp; tmp = ioread16(fpga_regs + 2*reg); return tmp; } static void fpga_set_bit(int reg, int bit) { unsigned long tmp = fpga_read_reg(reg); set_bit(bit, &tmp); fpga_write_reg(reg, tmp); } static void fpga_clear_bit(int reg, int bit) { unsigned long tmp = fpga_read_reg(reg); clear_bit(bit, &tmp); fpga_write_reg(reg, tmp); }
第äºã«ãã³ãã³ããšããŒã¿ãLCDã«çŽæ¥èšé²ããæ©èœã ãã£ã¹ãã¬ã€ãåæåããããã«äœ¿çšãããŸãã
é¢æ°ã¯çµ¶å¯Ÿã«ãäžåšçšãã§ã-ããŒã¿ã·ãŒãã«ç€ºãããŠããããã«ãã©ã³ã¶ã¯ã·ã§ã³ãäœæããŠãã ããïŒãã®èšäºã§ã¯ãã以äžã§ãïŒã
LCDããŒã¿/ã³ãã³ãæžã蟌ã¿æ©èœ
static void lcd_write_command(u16 val) { /* Write command code */ fpga_write_reg(LCD_DATA_CR, val); /* WR and RS low, RD high */ fpga_write_reg(LCD_CTRL_CR, LCD_CTRL_CR_RD); ndelay(1); /* RS low, WR and RD high */ fpga_write_reg(LCD_CTRL_CR, LCD_CTRL_CR_RD | LCD_CTRL_CR_WR); ndelay(1); /* All control signals high */ fpga_write_reg(LCD_CTRL_CR, LCD_CTRL_CR_RD | LCD_CTRL_CR_WR | LCD_CTRL_CR_RS); } static void lcd_write_data(u16 data) { /* Write data */ fpga_write_reg(LCD_DATA_CR, data); /* WR low, RD and RS high */ fpga_write_reg(LCD_CTRL_CR, LCD_CTRL_CR_RD | LCD_CTRL_CR_RS); ndelay(1); /* All control signals high */ fpga_write_reg(LCD_CTRL_CR, LCD_CTRL_CR_RD | LCD_CTRL_CR_RS | LCD_CTRL_CR_WR); }
å®ã¯ãåçŽãªLCDåæåã§ãã
LCDåæåæ©èœ
static void lcd_init(struct fb_info *info) { // Clear data fpga_write_reg(LCD_DATA_CR, 0); // All control signals high fpga_write_reg(LCD_CTRL_CR, LCD_CTRL_CR_RD | LCD_CTRL_CR_RS | LCD_CTRL_CR_WR); mdelay(100); lcd_write_command(ILI9341_DISPLAY_ON); lcd_write_command(ILI9341_SLEEP_OUT); lcd_write_command(ILI9341_INVERTION_OFF); lcd_write_command(ILI9341_MEM_ACCESS_CTRL); lcd_write_data(MY | MX | MV | BGR); lcd_write_command(ILI9341_PIXEL_FORMAT); lcd_write_data(0x0055); lcd_write_command(ILI9341_COLUMN_ADDR); lcd_write_data(0x0000); lcd_write_data(0x0000); lcd_write_data((DISPLAY_WIDTH-1) >> 8); lcd_write_data((DISPLAY_WIDTH-1) & 0xFF); lcd_write_command(ILI9341_PAGE_ADDR); lcd_write_data(0x0000); lcd_write_data(0x0000); lcd_write_data((DISPLAY_HEIGHT-1) >> 8); lcd_write_data((DISPLAY_HEIGHT-1) & 0xFF); lcd_write_command(ILI9341_MEM_WRITE); }
䜿çšãããã³ãã³ãã«ã€ããŠç°¡åã«èª¬æããŸãã
ILI9341_DISPLAY_ONïŒ0x29ïŒããã³ILI9341_SLEEP_OUTïŒ0x11ïŒãããã¯äºæ³å€ã§ããããã£ã¹ãã¬ã€ããªã³ã«ããŠèµ·åããŸãã
ILI9341_MEM_ACCESS_CTRLïŒ0x36ïŒ-ããã¯ã¡ã¢ãªã¹ãã£ã³ã®æ¹åã®èšå®ã§ãã
ILI9341_PIXEL_FORMATïŒ0x3aïŒ-ç»å圢åŒããã¯ã»ã«ããã16ãããã§ãã
ILI9341_COLUMN_ADDRïŒ0x2aïŒããã³ILI9341_PAGE_ADDRïŒ0x2bïŒã¯ããã£ã¹ãã¬ã€ã®äœæ¥é åãèšå®ããŸãã
ILI9341_MEM_WRITEïŒ0x2cïŒ-ãã®ã³ãã³ãã¯ãããŒã¿ãã©ã³ã¶ã¯ã·ã§ã³ãåŸã«ç¶ãããšã瀺ããŸãã ãã®å ŽåãçŸåšã®äœçœ®ã¯ãILI9341_COLUMN_ADDRãšILI9341_PAGE_ADDRã䜿çšããŠããããèšå®ãããåæã®åãšè¡ã«èšå®ãããŸãã åãã©ã³ã¶ã¯ã·ã§ã³ã®åŸãåã¯èªåçã«1ãã€å¢å ããŸããåãæåŸã®åãšçãããªããšã次ã®è¡ãžã®é·ç§»ãçºçããŸãã åãšè¡ã®äž¡æ¹ãæåŸã«çãããªããšãäœçœ®ã¯æåã®äœçœ®ã«æ»ããŸãã
ãããã£ãŠãILI9341_MEM_WRITEã³ãã³ãã®åŸãFPGAã®ã³ã³ãããŒã©ãŒã¯ãä»ã®ããšãæ°ã«ããã«ãã¡ã¢ãªããLCDã«ããŒã¿ãåã«ã埪ç°ããããããšãã§ããŸãã
ãã©ã€ããŒã§æåŸã«èå³ãããã®ã¯ããããŒãæ©èœã§ãã
ãã©ã€ããŒãããŒãæ©èœ
struct fb_info *info; int ret; u32 vmem_size; unsigned char *vmem; dma_addr_t dma_addr; pdev->dev.dma_mask = &platform_dma_mask; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); vmem_size = (etn_fb_var.width * etn_fb_var.height * etn_fb_var.bits_per_pixel) / 8; vmem = dmam_alloc_coherent(&pdev->dev, vmem_size, &dma_addr, GFP_KERNEL); if (!vmem) { dev_err(&pdev->dev, "FB: dma_alloc_coherent error\n"); return -ENOMEM; } memset(vmem, 0, vmem_size); info = framebuffer_alloc(0, &pdev->dev); if (!info) return -ENOMEM; info->screen_base = vmem; info->fbops = &etn_fb_ops; info->fix = etn_fb_fix; info->fix.smem_start = dma_addr; info->fix.smem_len = vmem_size; info->var = etn_fb_var; info->flags = FBINFO_DEFAULT; info->pseudo_palette = &etn_fb_pseudo_palette; /* Get FPGA registers address */ fpga_regs = devm_ioremap(&pdev->dev, FPGA_REGS_BASE, REGSIZE); /* Disable refreshing */ fpga_write_reg(LCD_DMA_CR, 0); lcd_init(info); set_dma_addr(dma_addr); set_fps(fps); /* Enable refreshing */ fpga_set_bit(LCD_DMA_CR, LCD_DMA_CR_REDRAW_EN); ret = register_framebuffer(info); if (ret < 0) { framebuffer_release(info); return ret; } platform_set_drvdata(pdev, info); return 0;
ããã§äœãèµ·ãã£ãŠããŸããïŒ
ãŸãã dmam_alloc_coherentïŒïŒé¢æ°ã䜿çšããŠãDMAäºæãŸãŒã³ã«ã¡ã¢ãªãå²ãåœãŠãŸãã ãã®å Žåãéžæããé åããæãã2ã€ã®ã¢ãã¬ã¹ãååŸããŸãã 1ã€ã¯ãã©ã€ããŒã§äœ¿çšããã2ã€ç®ã¯FPGAã«æžã蟌ã¿ãDMAã³ã³ãããŒã©ãŒããã®é åããããŒã¿ãèªã¿åããããã«ããŸãã
DMAãããã³ã°ã«ã€ããŠç°¡åã«èª¬æããŸãã ãããã«ã¯2ã€ã®ã¿ã€ãããããŸãã
- ã¹ããªãŒãã³ã°
- åæïŒäžè²«æ§ãŸãã¯äžè²«æ§ïŒ
äžè²«ãããããã³ã°ã¯ãããã»ããµãšããã€ã¹ã®äž¡æ¹ã§åæã«å©çšã§ããŸãã ã¢ã¯ã»ã¹ãããšãååœäºè ã¯ãæ°é®®ãªãããŒã¿ãåãåãããšãä¿èšŒãããŸãã ãããã¡ãŒããã©ã€ããŒã®åç¶æéãéããŠååšããå Žåã«æããã䜿çšãããŸãã 䜿çšäŸã¯ããã¬ãŒã ãããã¡ã¡ã¢ãªã§ãã
ã¹ããªãŒãã³ã°ãããã³ã°ã䜿çšããå Žåãå³å¯ã«é çªã«ã¢ã¯ã»ã¹ã§ããŸãã ã»ãšãã©ã®å Žåããããã¯1ã€ã®æäœäžã«äœæãããŸãã
çè«çã«ã¯ãããçç£çãããããŸããã äŸã¯ããããã¯ãŒã¯ãã±ããã®åä¿¡/éä¿¡ã§ãã
ãããŒãæ©èœã«æ»ããŸãã 次ã«ã fb_infoã«å ¥åããŸãã
次ã«ãã¹ããŒã¿ã¹å¶åŸ¡ã¬ãžã¹ã¿ãèªã¿æžãã§ããããã«ãFPGAã¢ãã¬ã¹ç©ºéããããããŸãã
ãã®åŸãå¿ èŠãªFPSå€ãšDMAã¢ãã¬ã¹ãFPGAã«æžã蟌ã¿ãŸãïŒå¿ èŠã«å¿ããŠãã¯ãŒãçªå·ã«å€æããããšãå¿ããªãã§ãã ããïŒã
次ã«ãFPGAã§ã®ã¬ã³ããªã³ã°ãæå¹ã«ããŠããã¬ãŒã ãããã¡ãŒãç»é²ããŸãã ãã¹ãŠæºåå®äºã§ãïŒ
FPGAã®ã¢ãžã¥ãŒã«
FPGAã®ã¢ãžã¥ãŒã«ã«å°éããŸããã ããã§ãããã¹ãŠãç°¡åã§ãã
å®è£ ããå¿ èŠãããããšãæãåºãããŠãã ããïŒ
- CPUããLCDãžã®çŽæ¥ã¢ã¯ã»ã¹
- ãã¬ãŒã ãããã¡ã¡ã¢ãªã®èªã¿åã
- LCDãžã®æžã蟌ã¿ãã©ã³ã¶ã¯ã·ã§ã³ã®åœ¢æ
- é©åãªFPSãååŸãã
åœç¶ãå¶åŸ¡ã¬ãžã¹ã¿ã¯CPUããLCDãžã®çŽæ¥ã¢ã¯ã»ã¹ãæäŸããããã«äœ¿çšãããŸãã
ãããŠãéåžžã®ãã«ããã¬ã¯ãµ-å¶åŸ¡ãCPUããæ¥ãå Žåãã¬ãžã¹ã¿ããã®ä¿¡å·ã¯LCDãžã®ã€ã³ã¿ãŒãã§ãŒã¹ã«åãæ¿ããããããã§ãªãå Žåã¯-ã¢ãžã¥ãŒã«ããFPGAãžã®ä¿¡å·ã éžæã¯ãã¹ããŒããã·ã³ã®ç¶æ ã«ãã£ãŠç°ãªããŸããããã«ã€ããŠã¯ã以äžã§èª¬æããŸãã
ã³ãŒãã¯ããªããã£ãã§ãïŒ
LCDãã¹MUX
always_ff @( posedge clk_i ) if( state == IDLE_S ) begin lcd_bus_if.data <= lcd_ctrl_if.data; lcd_bus_if.rd <= lcd_ctrl_if.rd; lcd_bus_if.wr <= lcd_ctrl_if.wr; lcd_bus_if.rs <= lcd_ctrl_if.rs; end else // Send data transactions from FPGA. begin lcd_bus_if.data <= lcd_data_from_fpga; lcd_bus_if.rd <= 1'b1; lcd_bus_if.wr <= lcd_wr_from_fpga; lcd_bus_if.rs <= 1'b1; end
次ã®ã¿ã¹ã¯ã¯ãã¡ã¢ãªããããŒã¿ãèªã¿åããLCDã«æžã蟌ãããšã§ãã ããã§å°ãèããå¿ èŠããããŸãã
èªã¿åãã€ã³ã¿ãŒãã§ã€ã¹ã®ã¹ã«ãŒãããã¯ãLCDã«ããŒã¿ãæžã蟌ãé床ãããã¯ããã«å€§ãããããé£ç¶ããŠããŒã¿ãèªã¿åãããšã¯ã§ããŸããïŒããã¥ã¡ã³ãã«ç€ºãããŠããã¿ã€ã ã¹ã¿ã³ãã確èªããå¿ èŠãããããšã«æ³šæããŠãã ããïŒã
ã€ãŸãã人çºçã«èªã¿åãé床ãå¶éããå¿ èŠããããŸãã ããã«ã¯æ¬¡ã®ãªãã·ã§ã³ããããŸãã
- LCDãžã®èªã¿åããšæžã蟌ã¿-èªã¿åããæžã蟌ã¿ãèªã¿åããæžã蟌ã¿ãªã©
- ããŒã¿ãèªã¿åããç¶æããå¿ èŠãããé床ãèšç®ããŸã
- FIFOã䜿çš
æåã®ãªãã·ã§ã³ã¯ãLCDäžã®ããŒã¿ã«å€§ããªïŒFPGAèŠæ Œã«ããïŒäžæåæ¢ãçºçãããšããäºå®ã«ã€ãªãããŸãã
é©çšãããåé¡ïŒ50ãè¶ ããFPSãååŸããå¿ èŠã¯ã»ãšãã©ãããŸããïŒãèæ ®ãããšãããã§ååã§ããå¯èœæ§ãååã«ãããŸãã
ããããããã¯éåžžã«äžåšçšã§ugãã§ãã ãããã£ãŠããã®ãªãã·ã§ã³ã¯ç Žæ£ããŸãã
2çªç®ã®ãªãã·ã§ã³ã¯ãLCDã«é£ç¶ã¹ããªãŒã ãååŸã§ããããã«ãã¡ã¢ãªããããŒã¿ãèªã¿åãã®ã«å¿ èŠãªé床ãèšç®ããããšã§ãã ãŸããç¹ã«åºåã¹ããªãŒã ã®é床ã®äžå®æ§ã«é¢ããå³å¯ãªèŠä»¶ããªãããšãèæ ®ãããšãéåžžã«æå¹ãªãªãã·ã§ã³ã§ãã ãã ããäžè¬çãªã±ãŒã¹ã§ã¯ãèªã¿åããã©ã³ã¶ã¯ã·ã§ã³ã®é 延ã®å€æ°å€ã«ããããããã¡ã䜿çšããŠé床ã調æŽããå¿ èŠããããŸãã
3çªç®ã®ãªãã·ã§ã³ã¯éåžžã«ã·ã³ãã«ã§ãããªãä¿¡é Œæ§ããããŸãã ãã®æ¬è³ªã¯ãã¡ã¢ãªããèªã¿åã£ãããŒã¿ãé«éã§æžã蟌ãFIFOã䜿çšããããšã§ãã ãããã¡ãŒã®ã¹ããŒã¹ããªããªããšãèªã¿åããäžæåæ¢ããŸãã åæã«ããããã¡ããããŒã¿ãé£ç¶ããŠèªã¿åããäžå®ã®é床ã§LCDã«ãã©ã³ã¶ã¯ã·ã§ã³ã圢æã§ããŸãã FIFOã«å Žæã衚瀺ããããšããã«ãåã³ã¡ã¢ãªããã®èªã¿åããåéããŸãã
3çªç®ã®ãªãã·ã§ã³ãéžæããŸãã æåã«FIFOãå¿ èŠã§ãã
FIFOã€ã³ã¹ã¿ã³ã¹
buf_fifo #( .AWIDTH ( FIFO_AWIDTH ), .DWIDTH ( AMM_DATA_W ) ) buf_fifo ( .clock ( clk_i ), .aclr ( ), .wrreq ( fifo_wr_req ), .data ( fifo_wr_data ), .rdreq ( fifo_rd_req ), .q ( fifo_rd_data ), .almost_full ( ), .full ( ), .empty ( fifo_empty ), .usedw ( fifo_usedw ) );
èªã¿åããäžæåæ¢ããã¿ã€ãã³ã°ãå€æããã«ã¯ããã§ã«ãã£ã±ãã«ãªã£ãŠããFIFOã®éãç¥ãã ãã§ã¯äžååã§ãã çµå±ãèªã¿åããã©ã³ã¶ã¯ã·ã§ã³ãããããããã¯çŸåšåŠçäžã§ãã ã€ãŸããèªã¿åããæ¢ã«èŠæ±ãããŠãããããŸã é ä¿¡ãããŠããªãããŒã¿ã§ãã
ãã®ãããªãã©ã³ã¶ã¯ã·ã§ã³ã®æ°ãçŸæç¹ã§ç¥ãå¿ èŠããããŸãã ãããè¡ãã«ã¯ãèªã¿åãèŠæ±ãå®è¡ããããã³ã«ã察å¿ããã«ãŠã³ã¿ãŒãå¢ãããèªã¿åãããŒã¿ã®ç¢ºèªãåãåã£ããããããæžãããŸãã
ä¿çäžã®ãã©ã³ã¶ã¯ã·ã§ã³ã®èšç®
// Count of read transactions in progress logic [FIFO_AWIDTH-1:0] pending_read_cnt; always_ff @( posedge clk_i ) case( { read_req_w, amm_if.read_data_val } ) 2'b01: pending_read_cnt <= pending_read_cnt - 1'd1; 2'b10: pending_read_cnt <= pending_read_cnt + 1'd1; endcase
ãã®çµæããåŠçäžãã«FIFOã«èšé²ãããåèªãšãã©ã³ã¶ã¯ã·ã§ã³ã®åèšãã©ã€ã³ãããã®æ·±ãã«ã»ãŒçãããªã£ããšãã«èªã¿åããåæ¢ããŸãã ãã»ãŒã50åã®èªç±ãªåèªãéžæããŸãã
èªãã®ãããã
logic stop_reading; assign stop_reading = ( pending_read_cnt + fifo_usedw ) > ( 2**FIFO_AWIDTH - 'd50 );
Avalon MMã§ã®èªã¿åããã©ã³ã¶ã¯ã·ã§ã³èªäœã®åœ¢æã¯åå§çã§ãã äž»ãªããšã¯ãã€ã³ã¿ãŒãã§ã€ã¹ã®ã¿ã€ãã«å¿ããŠã¢ãã¬ã¹ãæ£ããã€ã³ã¯ãªã¡ã³ãããããšã§ãïŒ fpga2sdramãŸãã¯fpga2hps ïŒã€ã³ã¿ãŒãã§ã€ã¹ããã³éãã®è©³çŽ°ã«ã€ããŠã¯ã ãã¡ããåç §ããŠãã ãã ïŒïŒ
èªã¿åããã©ã³ã¶ã¯ã·ã§ã³
// fpga2sdram used word address, so we must added 1 every time, // fpga2hps used byte address, so we must added 8 (for 64-bit iface). logic [31:0] addr_incr; assign addr_incr = ( USE_WORD_ADDRESS == 1 ) ? 1 : ( AMM_DATA_W >> 3 ); always_ff @( posedge clk_i ) if( state == IDLE_S ) amm_if.address <= lcd_ctrl_if.dma_addr; else if( read_req_w ) amm_if.address <= amm_if.address + addr_incr; // Always read all bytes in word assign amm_if.byte_enable = '1; // We don't use burst now assign amm_if.burst_count = 1; assign amm_if.read = ( state == READ_S ); // Remove Quartus warnings assign amm_if.write_data = '0; assign amm_if.write = 0;
ããŒã¿ãèªãããšãåŠãã ã®ã§ãä»åºŠã¯LCDã«ããŒã¿ãæžã蟌ãæ¹æ³ãåŠã¶å¿ èŠããããŸãã ãããè¡ãã«ã¯ã2ã€ã®ç¶æ ã®åçŽãªã¹ããŒããã·ã³ãäœæããŸããFIFOã«ããŒã¿ãããå Žåããã·ã³ã¯ãã©ã³ã¶ã¯ã·ã§ã³éä¿¡ç¶æ ã«ãªããŸãã ãããŠãèšé²ãçµäºãããšãIDLEã«æ»ããŸãã
LCDã«æžã蟌ãããã®FSM
enum int unsigned { LCD_IDLE_S, LCD_WRITE_S } lcd_state, lcd_next_state; always_ff @( posedge clk_i ) lcd_state <= lcd_next_state; always_comb begin lcd_next_state = lcd_state; case( lcd_state ) LCD_IDLE_S: begin if( !fifo_empty ) lcd_next_state = LCD_WRITE_S; end LCD_WRITE_S: begin if( lcd_word_cnt == 5'd31 ) lcd_next_state = LCD_IDLE_S; end endcase end assign fifo_rd_req = ( lcd_state == LCD_IDLE_S ) && ( lcd_next_state == LCD_WRITE_S );
LCDã16ãããã®ããŒã¿ã転éããåã«1ã€ã®ãã©ã³ã¶ã¯ã·ã§ã³ãèŠããŠããå¿ èŠããããFIFOã®åã¯ãŒãã®ãµã€ãºã¯64ãããã§ãïŒfpga2sdram / fpga2hpsã€ã³ã¿ãŒãã§ã€ã¹ã®æ§æã«ãã£ãŠç°ãªããŸãïŒã ãããã£ãŠãèªãåèªããšã«4ã€ã®ãã©ã³ã¶ã¯ã·ã§ã³ã圢æããŸãã
ããããç°¡åã«äœæã§ããŸãããã®ããã1ã€ã®ã«ãŠã³ã¿ãŒãäœæãããã®äžã®å¿ èŠãªãããã䜿çšããã ãã§ååã§ãã
èªã¿åããã©ã³ã¶ã¯ã·ã§ã³
// ILI9341 Data transaction from FPGA: // __ __ __ __ __ __ __ __ __ // clk/4 | __| |__| |__| |__| |__| |__| |__| |__| |__| | // // data | ///< split[0] | split[1] | split[2] | split[3] >//// // // _______________________________________________ // rd | xxxx xxxx // // _____ _____ _____ _____ // wr | xxxx_____| |_____| |_____| |_____| xxxx // // _______________________________________________ // rs | xxxx xxxx logic [3:0][15:0] fifo_rd_data_split; assign fifo_rd_data_split = fifo_rd_data; logic [15:0] lcd_data_from_fpga; logic lcd_wr_from_fpga; logic [4:0] lcd_word_cnt; always_ff @( posedge clk_i ) if( lcd_state == LCD_IDLE_S ) lcd_word_cnt <= '0; else lcd_word_cnt <= lcd_word_cnt + 1'd1; assign lcd_data_from_fpga = fifo_rd_data_split[ lcd_word_cnt[4:3] ]; assign lcd_wr_from_fpga = ( lcd_state == LCD_IDLE_S ) ? 1'b1 : lcd_word_cnt[2];
ã»ãšãã©ãã¹ãŠã äžèšã®ãã¹ãŠã管çããã¡ã€ã³ã¹ããŒããã·ã³ãäœæããŸãã
ãã®åäœã®ããžãã¯ã¯åçŽã§ã-LCDã³ã³ãããŒã©ã¢ãžã¥ãŒã«ããªã³ã«ãªã£ãŠããå Žåã1ã€ã®ãã¬ãŒã ãæç»ããå¿ èŠããããŸãã
ç¹å®ã®FPSãå®è£ ããããã«ãç®çã®ãã£ãã¯æ°ãäºæ³ããããäžæåæ¢ç¶æ ãããããŸãã
ãã®åŸãã¡ã¢ãªããã®ããŒã¿ã®èªã¿åããéå§ãããŸãïŒããŒã¿ãFIFOã«è¡šç€ºããããšããã«LCDãžã®æžã蟌ã¿ãèªåçã«éå§ãããŸãïŒã
ãã¬ãŒã å šäœãèªã¿åããããšãæ®ã£ãŠããã®ã¯LCDãžã®ãã©ã³ã¶ã¯ã·ã§ã³ã®å®äºãåŸ ã€ããšã ãã§ãã
ã¡ã€ã³FSM
logic [31:0] word_cnt; always_ff @( posedge clk_i ) if( state == IDLE_S ) word_cnt <= '0; else if( read_req_w ) word_cnt <= word_cnt + 1'd1; logic reading_is_finished; assign reading_is_finished = ( word_cnt == WORD_IN_FRAME - 1 ) && read_req_w; logic stop_reading; assign stop_reading = ( pending_read_cnt + fifo_usedw ) > ( 2**FIFO_AWIDTH - 'd50 ); logic all_is_finished; assign all_is_finished = ( pending_read_cnt == 0 ) && ( fifo_usedw == 0 ) && ( lcd_state == LCD_IDLE_S ); enum int unsigned { IDLE_S, FPS_DELAY_S, READ_S, WAIT_READIND_S, WAIT_WRITING_S } state, next_state; always_ff @( posedge clk_i ) state <= next_state; // FIXME: // If lcd_ctrl_if.redraw_en == 1 // CPU have one takt for read 0 in lcd_ctrl_if.dma_busy // Fix: add WAIT_WRITING_S -> FPS_DELAY_S path always_comb begin next_state = state; case( state ) IDLE_S: begin if( lcd_ctrl_if.redraw_stb || lcd_ctrl_if.redraw_en ) next_state = FPS_DELAY_S; end FPS_DELAY_S: begin if( fps_delay_done_w ) next_state = READ_S; end READ_S: begin if( reading_is_finished ) next_state = WAIT_WRITING_S; else if( stop_reading ) next_state = WAIT_READIND_S; end WAIT_READIND_S: begin if( !stop_reading ) next_state = READ_S; end WAIT_WRITING_S: begin if( all_is_finished ) next_state = IDLE_S; end endcase end
ããã§ãLCDã³ã³ãããŒã©ãŒã®æºåãæŽããŸããã
U-bootã»ããã¢ãã
åã®èšäºã§ ã fpga2sdramã€ã³ã¿ãŒãã§ãŒã¹ããªã³ã«ããããšã¯U-bootã§è¡ãå¿ èŠããããšæžããŸããã ããããªããšãèªã¿åããã©ã³ã¶ã¯ã·ã§ã³äžã«ãã·ã¹ãã ãå®å šã«ããªãŒãºããŸãã ãããè¡ãã«ã¯ãç°å¢ã«æ¬¡ã®è¡ãè¿œå ããŸãã
u-boot-env.txt
... fpgadata=0x10000000 fpgafile=/lib/firmware/fpga/fpga.rbf fpgaboot=setenv fpga2sdram_handoff 0x3fff; ext2load mmc 0:2 ${fpgadata} ${fpgafile}; fpga load 0 ${fpgadata} ${filesize} bridge_enable_handoff=mw $fpgaintf ${fpgaintf_handoff}; go $fpga2sdram_apply; mw $fpga2sdram ${fpga2sdram_handoff}; mw $axibridge ${axibridge_handoff}; mw $l3remap ${l3remap_handoff} bootcmd=run fpgaboot; run bridge_enable_handoff; run mmcboot ...
ãããã°
ååãšããŠããã¹ãŠãåé¡ãªãåäœããã¯ããªã®ã§ããããã°ãããã®ã¯äœããããŸããã
ããããç§ãã¡ã¯å°ãé¢åã§ãFPGAã¢ãžã¥ãŒã«ã®ãã¹ããã³ããäœæããªãã£ãã®ã§ãå®å¿ã®ããã«SignalTapã§ã¢ãžã¥ãŒã«ã®åäœãèŠã䟡å€ããããŸãã
ããã¯ãCPUããã®ãã©ã³ã¶ã¯ã·ã§ã³ã®å€èŠ³ã§ãã
ã³ãã³ã0x29ã0x11ã0x36ããã³ããŒã¿0xE8ã®èšé²ã衚瀺ãããŸãã ããã§ãã
ãããã£ãŠãFPGAããã®ãã©ã³ã¶ã¯ã·ã§ã³ã¯æ¬¡ã®ããã«ãªããŸãã
ãããŠãããã§ãããã¹ãŠãèšç»ã©ããã§ãã
ãã£ãïŒ FPGAã«LCDã³ã³ãããŒã©ãŒããããŸãã
æåŸãŸã§èªãã§ããã人ã«æè¬ããŸãïŒ é 匵ã£ãŠ
䟿å©ãªãªã³ã¯
githubã®ãœãŒã¹
ãã¹ãŠã®äœæ¥ãå®è¡ãããããã€ã¹
ãã¬ãŒã ãããã¡ãŒãã©ã€ããŒãèšè¿°ããããã®ããã¥ã¡ã³ã
ILI9341ããã¥ã¡ã³ã
åã®èšäºã«é¢ããã¡ã¢
ååã®èšäºã§ã¯ã fpga2sdramã€ã³ã¿ãŒãã§ãŒã¹ã®åž¯åå¹ ã枬å®ããŸããã
æ®å¿µãªãããç§ã¯ééããç¯ããŸããã ã€ãŸããPLLã·ã¥ã¬ããã¯å®éã®25 MHzã§ã¯ãªã125 MHzã«èšå®ãããŸããã
ãã®ãããPLLã®ä¹æ°ãšé€æ°ã®æ¯çã¯æ£ããèšç®ãããŸããã§ããã
ãã®çµæãDDR3ã¯èŠå®ã®333 MHzã§ã¯ãªã66 MHzã§åäœããŸããã
æ£ããä¿æ°ãš256ãããã®ã€ã³ã¿ãŒãã§ã€ã¹å¹ ã䜿çšãããšãã¹ã«ãŒãããã¯çŽ16ã17 Gbit / sã«ãªããŸããããã¯ãå¹ ã32ãããã§åšæ³¢æ°ã333 MHzã®DDR3ã€ã³ã¿ãŒãã§ã€ã¹ã®çè«äžã®ã¹ã«ãŒãããã«çžåœããŸãã
ãaã³ããŸãïŒ
å°èŠæš¡ãªèª¿æ»
ã³ãã¥ããã£ã®æèŠãç¥ãããã§ãã é£ãããªããã°ãæ祚ããŠãã ããã