📈
Ween's Lab
UdemyYouTubeTikTok
  • Welcome
  • 📻FPGA Tutorials
    • FPGA Boards: Getting Started
      • Getting Started with PYNQ on Kria KV260 Vision AI Starter Kit
      • Getting Started with PYNQ on Red Pitaya STEMlab 125-14
      • Getting Started with PYNQ on ZYBO
    • FPGA Ethernet Tutorial
      • FPGA Tutorial Ethernet 1: Simple TCP Server
    • PYNQ FPGA Tutorial 101
      • Part 0: Introduction
      • Part 1: GPIO
      • Part 2: Custom IP
      • Part 3: Memory
      • Part 4: ANN Processor
    • PYNQ FPGA Tutorial 102
      • Part 0: Introduction
      • Part 1: Memory Mapped
      • Part 2: Direct Memory Access
      • Part 3: AXI-Lite Multiplier
      • Part 4: AXI-Stream Multiplier with DMA
      • Part 5: AXI-Lite GCD
      • Part 6: AXI-Stream GCD with DMA
      • Part 7: Access to DDR from PL
    • ZYNQ FPGA Tutorial
      • Part 1: Gate-Level Combinational Circuit
      • Part 2: RT-Level Combinational Circuit
      • Part 3: Regular Sequential Circuit
      • Part 4: FSM Sequential Circuit
      • Part 5: ZYNQ Architecture
      • Part 6: ARM CPU and FPGA Module
      • Part 7: FPGA Memory
      • Part 8: Hardware Accelerator for Neural Networks
    • ZYNQ FPGA Linux Kernel Module
      • Cross Compiling Kernel, Kernel Module, and User Program for PYNQ
      • Configure PL to PS Interrupt in Kernel Module
      • Configure AXI DMA in Kernel Module
  • 📟Proyek Arduino
    • Kumpulan Proyek
      • Rangkaian LED
      • LED Berkedip Nyala Api
      • LED Chaser
      • LED Binary Counter
      • OLED 128x4 Bitcoin Ticker
      • Rangkaian Button
      • Button Multifungsi
      • Button Interrupt
      • Button Debouncing
    • Pelatihan Mikrokontroler Arduino ESP32
      • Bab 1 Pengenalan Mikrokontroler
      • Bab 2 Pengenalan Arduino
      • Bab 3 Pengenalan Bahasa C
      • Bab 4 Digital Output
      • Bab 5 Digital Input
      • Bab 6 Serial Communication
      • Bab 7 Analog-to-Digital Conversion
      • Bab 8 Interrupt
      • Bab 9 Timer
      • Bab 10 Pulse-Width Modulation
      • Bab 11 SPI Communication
      • Bab 12 I2C Communication
  • 💰Finance
    • Coding for Finance
      • Build a Bitcoin Price Alert with Google Cloud and Telegram
      • Build a Bitcoin Ticker with ESP32 and Arduino
      • Stock Price Forecasting with LSTM
    • Trading dan Investasi
      • Istilah Ekonomi, Keuangan, Bisnis, Trading, dan Investasi
      • Jalan Menuju Financial Abundance
      • Memahami Korelasi Emas, Oil, Dollar, BTC, Bonds, dan Saham
      • Mindset Trading dan Investasi
      • Rangkuman Buku: Rahasia Analisis Fundamental Saham
      • Rangkuman Buku: The Psychology of Money
      • Rangkuman Kuliah: Introduction to Adaptive Markets
      • Rumus Menjadi Orang Kaya
  • 📝Life
    • Life Quotes
Powered by GitBook
On this page
  • Objective
  • Source Code
  • 1. Hardware Design
  • 1.1. RTL Design of Image Invert Core
  • 1.2. Controller to Access DDR Memory
  • 1.3. System Design
  • 2. Software Design
  • 3. Full Step-by-Step Tutorial
  • 4. Conclusion
  1. FPGA Tutorials
  2. PYNQ FPGA Tutorial 102

Part 7: Access to DDR from PL

PreviousPart 6: AXI-Stream GCD with DMANextZYNQ FPGA Tutorial

Last updated 2 months ago

Objective

This tutorial contains information on how to create a module in PL that can access to DDR memory without AXI DMA. The module does a black and white image invert operation.

Source Code

This repository contains all of the code required in order to follow this tutorial.


1. Hardware Design

1.1. RTL Design of Image Invert Core

This RTL module does a simple image invert operation for black and white image. It has one 8-bit input and one 8-bit output. Every input will be subtracted from 255 to produce the output.

axis_img_inv.v
module axis_img_inv
    (
        // ### Clock and reset signals #########################################
        input  wire       aclk,
        input  wire       aresetn,
        // ### AXI4-stream slave signals #######################################
        output wire       s_axis_tready,
        input wire [7:0]  s_axis_tdata,
        input wire [0:0]  s_axis_tkeep,
        input wire        s_axis_tvalid,
        input wire        s_axis_tlast,
        // ### AXI4-stream master signals ######################################
        input wire        m_axis_tready,
        output wire [7:0] m_axis_tdata,
        output wire [0:0] m_axis_tkeep,
        output wire       m_axis_tvalid,
        output wire       m_axis_tlast
    );
    
    assign s_axis_tready = m_axis_tready;
    assign m_axis_tdata = 255 - s_axis_tdata;
    assign m_axis_tkeep = s_axis_tkeep;
    assign m_axis_tvalid = s_axis_tvalid;
    assign m_axis_tlast = s_axis_tlast;
    
endmodule

1.2. Controller to Access DDR Memory

To access DDR memory from PL, we are going to use AXI Data Mover IP. This IP handles the AXI-Full protocol to provide access to DDR memory.

To use the AXI Data Mover IP, we need a simple state machine that generated instruction to control the AXI Data Mover IP.

The state machine consists of core (img_inv_ctrl.v) and AXI-Lite wrapper (axi_img_inv_ctrl.v).

This is the code for img_inv_ctrl.v.

img_inv_ctrl.v
`timescale 1ns / 1ps

module img_inv_ctrl
    (
        input wire         clk,
        input wire         rst_n,
        output wire        ready,
        input wire [31:0]  saddr,
        input wire [31:0]  daddr,
        input wire [31:0]  btt,
        input wire         start,
        output wire [71:0] m_axis_mm2s_cmd_tdata,
        output wire        m_axis_mm2s_cmd_tvalid,
        output wire [71:0] m_axis_s2mm_cmd_tdata,
        output wire        m_axis_s2mm_cmd_tvalid,
        input wire         s2mm_wr_xfer_cmplt
    );
    
    reg [2:0] state_reg, state_next;
    
    always @(posedge clk)
    begin
        if (!rst_n)
        begin
            state_reg <= 0;
        end
        else
        begin
            state_reg <= state_next;
        end
    end
    
    always @(*)
    begin
        state_next = state_reg;
        case (state_reg)
            0: // Wait for start from PS
            begin
                if (start)
                begin
                    state_next = 1;
                end
            end
            1: // Send S2MM instruction
            begin
                state_next = 2;
            end
            2: // Send MM2S instruction
            begin
                state_next = 3;
            end
            3: // Wait until S2MM transfer is completed
            begin
                if (s2mm_wr_xfer_cmplt)
                begin
                    state_next = 0; // Back to idle
                end
            end
        endcase
    end
    
    assign ready = (state_reg == 0) ? 1 : 0;
    
    assign m_axis_s2mm_cmd_tdata = {8'b00000000, daddr, 1'b0, 1'b1, 6'b000000, 1'b1, btt[22:0]}; // S2MM instruction
    assign m_axis_s2mm_cmd_tvalid = (state_reg == 1) ? 1 : 0; // Send S2MM instruction
    assign m_axis_mm2s_cmd_tdata = {8'b00000000, saddr, 1'b0, 1'b1, 6'b000000, 1'b1, btt[22:0]}; // MM2S instruction
    assign m_axis_mm2s_cmd_tvalid = (state_reg == 2) ? 1 : 0; // Send MM2S instruction
    
endmodule

This is the code for axi_img_inv_ctrl.v.

axi_img_inv_ctrl.v
module axi_img_inv_ctrl
    (
        // ### Clock and reset signals #########################################
        input  wire        aclk,
        input  wire        aresetn,
        // ### AXI4-lite slave signals #########################################
        // *** Write address signals ***
        output wire        s_axi_awready,
        input  wire [31:0] s_axi_awaddr,
        input  wire        s_axi_awvalid,
        // *** Write data signals ***
        output wire        s_axi_wready,
        input  wire [31:0] s_axi_wdata,
        input  wire [3:0]  s_axi_wstrb,
        input  wire        s_axi_wvalid,
        // *** Write response signals ***
        input  wire        s_axi_bready,
        output wire [1:0]  s_axi_bresp,
        output wire        s_axi_bvalid,
        // *** Read address signals ***
        output wire        s_axi_arready,
        input  wire [31:0] s_axi_araddr,
        input  wire        s_axi_arvalid,
        // *** Read data signals ***	
        input  wire        s_axi_rready,
        output wire [31:0] s_axi_rdata,
        output wire [1:0]  s_axi_rresp,
        output wire        s_axi_rvalid,
        // ### User signals ####################################################
        output wire [71:0] m_axis_mm2s_cmd_tdata,
        output wire        m_axis_mm2s_cmd_tvalid,
        output wire [71:0] m_axis_s2mm_cmd_tdata,
        output wire        m_axis_s2mm_cmd_tvalid,
        input wire         s2mm_wr_xfer_cmplt
    );

    // ### Register map ########################################################
    // 0x00: source address
    //       bit 31~0 = saddr[31:0] (R/W)
    // 0x04: destination address
    //       bit 31~0 = daddr[31:0] (R/W)
    // 0x08: byte to transfer
    //       bit 22~0 = btt[22:0] (R/W)
    // 0x0c: control
    //       bit 0 = START (R/W)
    //       bit 1 = READY (R)
    localparam C_ADDR_BITS = 8;
    //    // *** Address (32-bit) ***
//    localparam C_ADDR_SADDR = 8'h00,
//               C_ADDR_DADDR = 8'h04,
//               C_ADDR_BTT   = 8'h08,
//               C_ADDR_CTRL  = 8'h0c;
    // *** Address (40-bit) ***
    localparam C_ADDR_SADDR = 8'h00,
               C_ADDR_DADDR = 8'h08,
               C_ADDR_BTT   = 8'h10,
               C_ADDR_CTRL  = 8'h18;
    // *** AXI write FSM ***
    localparam S_WRIDLE = 2'd0,
               S_WRDATA = 2'd1,
               S_WRRESP = 2'd2;
    // *** AXI read FSM ***
    localparam S_RDIDLE = 2'd0,
               S_RDDATA = 2'd1;
    
    // *** AXI write ***
    reg [1:0] wstate_cs, wstate_ns;
    reg [C_ADDR_BITS-1:0] waddr;
    wire [31:0] wmask;
    wire aw_hs, w_hs;
    // *** AXI read ***
    reg [1:0] rstate_cs, rstate_ns;
    wire [C_ADDR_BITS-1:0] raddr;
    reg [31:0] rdata;
    wire ar_hs;
    // *** Control registers ***
    reg start_reg;
    wire ready_w;
    reg [31:0] saddr_reg;
    reg [31:0] daddr_reg;
    reg [31:0] btt_reg;
    
    // ### AXI write ###########################################################
    assign s_axi_awready = (wstate_cs == S_WRIDLE);
    assign s_axi_wready = (wstate_cs == S_WRDATA);
    assign s_axi_bresp = 2'b00;    // OKAY
    assign s_axi_bvalid = (wstate_cs == S_WRRESP);
    assign wmask = {{8{s_axi_wstrb[3]}}, {8{s_axi_wstrb[2]}}, {8{s_axi_wstrb[1]}}, {8{s_axi_wstrb[0]}}};
    assign aw_hs = s_axi_awvalid & s_axi_awready;
    assign w_hs = s_axi_wvalid & s_axi_wready;

    // *** Write state register ***
    always @(posedge aclk)
    begin
        if (!aresetn)
            wstate_cs <= S_WRIDLE;
        else
            wstate_cs <= wstate_ns;
    end
    
    // *** Write state next ***
    always @(*)
    begin
        case (wstate_cs)
            S_WRIDLE:
                if (s_axi_awvalid)
                    wstate_ns = S_WRDATA;
                else
                    wstate_ns = S_WRIDLE;
            S_WRDATA:
                if (s_axi_wvalid)
                    wstate_ns = S_WRRESP;
                else
                    wstate_ns = S_WRDATA;
            S_WRRESP:
                if (s_axi_bready)
                    wstate_ns = S_WRIDLE;
                else
                    wstate_ns = S_WRRESP;
            default:
                wstate_ns = S_WRIDLE;
        endcase
    end
    
    // *** Write address register ***
    always @(posedge aclk)
    begin
        if (aw_hs)
            waddr <= s_axi_awaddr[C_ADDR_BITS-1:0];
    end
    
    // ### AXI read ############################################################
    assign s_axi_arready = (rstate_cs == S_RDIDLE);
    assign s_axi_rdata = rdata;
    assign s_axi_rresp = 2'b00;    // OKAY
    assign s_axi_rvalid = (rstate_cs == S_RDDATA);
    assign ar_hs = s_axi_arvalid & s_axi_arready;
    assign raddr = s_axi_araddr[C_ADDR_BITS-1:0];
    
    // *** Read state register ***
    always @(posedge aclk)
    begin
        if (!aresetn)
            rstate_cs <= S_RDIDLE;
        else
            rstate_cs <= rstate_ns;
    end

    // *** Read state next ***
    always @(*) 
    begin
        case (rstate_cs)
            S_RDIDLE:
                if (s_axi_arvalid)
                    rstate_ns = S_RDDATA;
                else
                    rstate_ns = S_RDIDLE;
            S_RDDATA:
                if (s_axi_rready)
                    rstate_ns = S_RDIDLE;
                else
                    rstate_ns = S_RDDATA;
            default:
                rstate_ns = S_RDIDLE;
        endcase
    end
    
    // *** Read data register ***
    always @(posedge aclk)
    begin
        if (!aresetn)
            rdata <= 0;
        else if (ar_hs)
            case (raddr)
                C_ADDR_SADDR: 
                    rdata <= saddr_reg;
                C_ADDR_DADDR: 
                    rdata <= daddr_reg;
                C_ADDR_BTT:
                    rdata <= btt_reg;
                C_ADDR_CTRL:
                    rdata <= {{30{1'b0}}, ready_w, start_reg};
            endcase
    end
    
    // ### User design #########################################################
    // *** Start register ***
    always @(posedge aclk)
    begin
        if (!aresetn)
        begin
            start_reg <= 0;
        end
        else if (w_hs && waddr == C_ADDR_CTRL && s_axi_wdata[0])
        begin
            start_reg <= 1;
        end
        else
        begin
            start_reg <= 0;
        end
    end

    // *** Register saddr and btt ***
    always @(posedge aclk)
    begin
        if (!aresetn)
        begin
            saddr_reg[31:0] <= 0;
            daddr_reg[31:0] <= 0;
            btt_reg[31:0] <= 0;
        end
        else if (w_hs && waddr == C_ADDR_SADDR)
        begin
            saddr_reg[31:0] <= (s_axi_wdata[31:0] & wmask) | (saddr_reg[31:0] & ~wmask);
        end
        else if (w_hs && waddr == C_ADDR_DADDR)
        begin
            daddr_reg[31:0] <= (s_axi_wdata[31:0] & wmask) | (daddr_reg[31:0] & ~wmask);
        end
        else if (w_hs && waddr == C_ADDR_BTT)
        begin
            btt_reg[31:0] <= (s_axi_wdata[31:0] & wmask) | (btt_reg[31:0] & ~wmask);
        end
    end

    img_inv_ctrl img_inv_ctrl_0
    (
        .clk(aclk),
        .rst_n(aresetn),
        .ready(ready_w),
        .saddr(saddr_reg),
        .daddr(daddr_reg),
        .btt(btt_reg),
        .start(start_reg),
        .m_axis_mm2s_cmd_tdata(m_axis_mm2s_cmd_tdata),
        .m_axis_mm2s_cmd_tvalid(m_axis_mm2s_cmd_tvalid),
        .m_axis_s2mm_cmd_tdata(m_axis_s2mm_cmd_tdata),
        .m_axis_s2mm_cmd_tvalid(m_axis_s2mm_cmd_tvalid),
        .s2mm_wr_xfer_cmplt(s2mm_wr_xfer_cmplt)
    );

endmodule

1.3. System Design

This diagram shows our system. It consists of an ARM CPU, DRAM, AXI Image Invert Control (), AXI Data Mover, and our AXI-Stream image invert module. Our AXI-Stream image invert module is connected to the AXI Data Mover. Between them, we also add AXI-Stream FIFO IP.

The following figure shows the Zynq IP high-performance port configuration. There are two pots enabled, which are AXI HP0 and AXI HP2.

The following figure shows the AXI Data Mover configuration.

This is the final block design diagram as shown in Vivado.

2. Software Design

First, we need to create access to the state machine module for the AXI Data Mover. Because it is just a memory map IP, so we use MMIO.

# Access to memory map of the axi_inv_img_ctrl 
ADDR_BASE = 0xA0000000
ADDR_RANGE = 0x80
ctrl_obj = MMIO(ADDR_BASE, ADDR_RANGE)

Prepare the input image. Declare several black and white image as arrays. The image is MNIST digit image 28x28 pixels.

# MNIST image as text
img_txt_1 = b'0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,253,255,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,244,251,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,251,251,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,236,251,211,31,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,251,251,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,253,253,189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,253,251,235,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,205,253,251,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,251,253,184,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,240,251,193,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,253,253,253,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,151,251,251,251,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,221,251,251,172,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,234,251,251,196,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,251,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,255,253,253,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,228,253,247,140,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,193,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
img_txt_6 = b'0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,222,225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,234,252,176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,197,253,252,208,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,178,252,253,117,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,252,252,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,222,253,253,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,252,179,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198,246,220,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,253,252,135,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,140,253,252,118,0,0,0,0,111,140,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,191,255,253,56,0,0,114,113,222,253,253,255,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,252,253,223,37,0,48,174,252,252,242,214,253,199,31,0,0,0,0,0,0,0,0,0,0,0,0,13,109,252,228,130,0,38,165,253,233,164,49,63,253,214,31,0,0,0,0,0,0,0,0,0,0,0,0,73,252,252,126,0,23,178,252,240,148,7,44,215,240,148,0,0,0,0,0,0,0,0,0,0,0,0,0,119,252,252,0,0,197,252,252,63,0,57,252,252,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,253,174,0,48,229,253,112,0,38,222,253,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,252,173,0,48,227,252,158,226,234,201,27,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,252,252,57,104,240,252,252,253,233,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,242,252,253,252,252,252,252,240,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,189,253,252,252,157,112,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
img_txt_8 = b'0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,203,229,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,47,47,30,95,254,215,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,154,185,185,223,253,253,133,175,255,188,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,253,253,253,246,161,228,253,253,254,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,245,253,158,137,21,0,48,233,253,233,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,254,223,25,0,0,36,170,254,244,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,212,253,161,11,26,178,253,236,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,155,253,228,80,223,253,253,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,253,253,254,253,154,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,253,253,253,254,179,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,171,254,254,254,179,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,253,253,253,253,178,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,123,254,253,203,156,253,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,254,121,13,93,253,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,239,253,76,8,32,219,253,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,254,191,0,5,108,234,254,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,253,190,5,85,253,236,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,253,169,192,253,253,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,253,253,254,236,129,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,118,243,191,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'

# Convert MNIST image from text to numpy array 
img_bytes = np.array([int(i) for i in img_txt_8.decode('utf-8').split(',')], dtype=np.uint8)
img_bytes.shape

# Convert image array to 2D for plotting
img_bytes_2d = np.reshape(img_bytes, (28, 28))
img_bytes_2d.shape

# Plot original image before inversion
pyplot.imshow(img_bytes_2d, cmap=pyplot.get_cmap('gray'))

This is the input image plotted by pyplot.

Process the image bytes with the image invert module.

# Allocate physical memory for AXI data mover MM2S and S2MM
input_buffer = allocate(shape=img_bytes.shape, dtype=np.uint8)
output_buffer = allocate(shape=img_bytes.shape, dtype=np.uint8)

# Copy original image from numpy array to physical memory
np.copyto(input_buffer, img_bytes)
print("Input buffer address   :", hex(input_buffer.physical_address))

# Setup AXI data mover instruction
ctrl_obj.write(0x0, input_buffer.physical_address)
ctrl_obj.write(0x8, output_buffer.physical_address)
ctrl_obj.write(0x10, len(img_bytes))

# Start main controller
ctrl_obj.write(0x18, 1)
# Wait until ready flag is 1
while ((ctrl_obj.read(0x18) & (1 << 1)) == 0):
    pass

Read and check the result.

# Convert inverted image array to 2D for plotting
img_bytes_2d_inv = np.reshape(output_buffer, (28, 28))
img_bytes_2d_inv.shape

# Plot image after inversion
pyplot.imshow(img_bytes_2d_inv, cmap=pyplot.get_cmap('gray'))

# Delete buffer to prevent memory leak
del input_buffer, output_buffer

This is the output image plotted by pyplot.

3. Full Step-by-Step Tutorial

This video contains detailed steps for making this project.

4. Conclusion

In this tutorial, we covered a tutorial on how to create a module in PL that can access to DDR memory without AXI DMA, but using AXI Data Mover. This method is suitable if the main control in in PL.

📻
GitHub - weenslab/pynq102GitHub
Logo