Memory write

Hello!
I’m trying to write some data into memory using APB but it seems like nothing is written. I’ve also tried a simple mem write command :
sudo devmem2 0x41100010 w 0x1
/dev/mem opened.
Memory mapped at address 0x3fa6951000.
Value at address 0x41100010 (0x3fa6951010): 0x0
Written 0x1; readback 0x0
Can somebody explain why doesn’t this work and how to fix it?

1 Like

Considering what you gave us to work with, I’d have to say “no” and “no”.

There are just too many ways how to not make it work…

The URL to your design repository would be a great start!

I understand! Here is the link to my repository, it’s a fork from the one in the documentation. and I’ve only created my own cape for implementing the color space conversion. I’ve tried to wire a led to psel to see if the selection works. sources/FPGA-design/script_support/components/CAPE/YUV422_RGB/HDL · main · Luciana Mitu / Testing_gateware · GitLab
And also, this is my c program that starts the conversion: #include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>
#include <poll.h>
#include <string.h>

#define FPGA_BASE 0x41000000 // New address
#define MEM_SIZE 0x1000
#define WIDTH 176
#define HEIGHT 144
#define FRAME_BYTES (WIDTH * HEIGHT * 2)

#define FPGA_IRQ 53

volatile int data_ready_ack = 0;
volatile int conv_done_ack = 0;

volatile unsigned int *fpga = NULL;

void irq_handler(int signum) {
unsigned int status = fpga[10];
printf(“IRQ Status (fpga[10]): 0x%08x\n”, status);
if (status & 0x1) {
printf(“Interrupt: YUV data loaded\n”);
data_ready_ack = 1;
fpga[10] = 0x1;
}
if (status & 0x2) {
printf(“Interrupt: Conversion completed\n”);
conv_done_ack = 1;
fpga[10] = 0x2;
}
}

int check_irq(void) {
unsigned int status = fpga[10];
printf(“Checking IRQ status (fpga[10]): 0x%08x\n”, status);
if (status & 0x3) {
irq_handler(0);
return 1;
}
return 0;
}

int main() {
int fd = open(“/dev/mem”, O_RDWR | O_SYNC);
if (fd < 0) {
perror(“Cannot open /dev/mem”);
return 1;
}

fpga = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, FPGA_BASE);
if (fpga == MAP_FAILED) { 
    perror("mmap failed");
    close(fd);
    return 1;
}

printf("Configuring IRQ (simulated for IRQ 53)...\n");

FILE *yuv_file = fopen("tulips_yuyv422_prog_packed_qcif.yuv", "rb");
if (!yuv_file) { 
    perror("Cannot open YUV file");
    close(fd);
    return 1;
}

FILE *rgb_file = fopen("output.rgb", "wb");
if (!rgb_file) { 
    perror("Cannot open RGB file");
    fclose(yuv_file);
    close(fd);
    return 1;
}

int byte_count = 0;

while (byte_count < FRAME_BYTES) {
    for (int i = 0; i < 8; i++) {
        int byte = fgetc(yuv_file);
        if (byte == EOF) byte = 0;
        printf("Writing at offset %d: 0x%02x\n", i * 4, byte);
        fpga[i] = byte;
        usleep(1000);
        printf("Address after writing %d: %d\n", i, fpga[9] & 0x7);
    }

    printf("Waiting for interrupt: YUV data loaded...\n");
    data_ready_ack = 0;
    int timeout = 10000;
    while (!data_ready_ack && timeout > 0) {
        if (check_irq()) {
            if (data_ready_ack) break;
        }
        usleep(100);
        timeout--;
    }
    if (timeout == 0) {
        printf("Timeout: Did not receive 'YUV data loaded' interrupt\n");
        break;
    }

    printf("Waiting for interrupt: Conversion completed...\n");
    conv_done_ack = 0;
    timeout = 10000;
    while (!conv_done_ack && timeout > 0) {
        if (check_irq()) {
            if (conv_done_ack) break;
        }
        usleep(100);
        timeout--;
    }
    if (timeout == 0) {
        printf("Timeout: Did not receive 'Conversion completed' interrupt\n");
        break;
    }

    for (int i = 0; i < 4; i++) {
        unsigned char r = (fpga[i] >> 24) & 0xFF;
        unsigned char g = (fpga[i] >> 16) & 0xFF;
        unsigned char b = (fpga[i] >> 8) & 0xFF;
        printf("Pixel %d: R=%02x, G=%02x, B=%02x\n", i, r, g, b);
        fwrite(&r, 1, 1, rgb_file);
        fwrite(&g, 1, 1, rgb_file);
        fwrite(&b, 1, 1, rgb_file);
    }

    byte_count += 8;
}

fclose(yuv_file);
fclose(rgb_file);
munmap((void*)fpga, MEM_SIZE);
close(fd);
printf("Conversion complete!\n");
return 0;

}
And also the pdf with the memory map from libero is attached.
MY_CUSTOM_FPGA_DESIGN_E4C803D2_memory_map.json (18.6 KO)

Here’s a very simple APB module I created to control a RISC-V processor:

Some things I noted:
You’re doing full address decoding; don’t do that, just the interesting bits,
i.e. 23-2; don’t try to decode bits 0 and 1, you’re handling 32-bits wide data.
Just trust that bits 31-24 will be decoded by whatever APB_MTARGET you connect to.

module ControlAPB #(
   parameter SELECT_BIT = 10
)(
   input  wire                  PCLK
  ,input  wire                  PRESETN
  // APB Slave
  ,input  wire           [31:0] PADDR
  ,input  wire                  PENABLE
  ,output wire           [31:0] PRDATA
  ,output wire                  PREADY
  ,input  wire                  PSEL
  ,output wire                  PSLVERR
  ,input  wire           [31:0] PWDATA
  ,input  wire                  PWRITE
  // MEMORY
  ,input  wire           [31:0] M_IN_0
  ,input  wire           [31:0] M_IN_1
  ,output wire [SELECT_BIT-1:0] M_ADDR
  ,output wire           [31:0] M_OUT
  ,output wire            [1:0] M_WEN
  ,output wire            [3:0] M_WBE
  // PRC
  ,output wire                  PRESETN_O
  ,output wire                  IE_O
  ,output wire           [23:0] START_PC_O
);

   assign PSLVERR = 1'b0;
   assign PREADY  = 1'b1;

   assign M_ADDR  = PADDR[SELECT_BIT+2:2];
   assign M_OUT   = PWDATA;

   assign M_WEN   = &{PENABLE, PWRITE, PSEL};

   assign M_WBE   = 4'b1111;

// +--------------------------------------+
// | R I       |            PC            |
// +-----------+- ------------------------+
// | 3 3 222222|22221111_11111100_00000000|
// | 1 0 987654|32109876_54321098_76543210|
// +-----------+--------------------------+
//
// Note: PC is shifted << 8 to make it 32bit,
//       so the execution granularity is 256 bytes.
//
   reg [31:0] res_int_pc_start;

   always @(posedge PCLK) begin
      if (~PRESETN) begin
         res_int_pc_start <= 32'b10_000000_10000000_00000000_00000000;
      end else begin
         if(M_WEN & PADDR[23:20] == 4'b0001) begin
            res_int_pc_start[31:30] <= PWDATA[31:30];
            if (res_int_pc_start[31] == 1'b0) begin
               res_int_pc_start[23:0] <= PWDATA[23:0]; // Only accept new PC if the CPU is *already* halted.
            end
         end
      end
   end

   assign PRESETN_O   = &{PRESETN, res_int_pc_start[31]};
   assign IE_O        = &{PRESETN, res_int_pc_start[30]};
   assign START_PC_O  = res_int_pc_start[23:0];

   assign PRDATA      = (PADDR[23:20] == 4'b0001) ? res_int_pc_start :
                        (PADDR[SELECT_BIT]) ? M_IN_1 : M_IN_0;

endmodule

This is how it’s connected:

So the gist of it is: Don’t decode more than the bits you really need,
and don’t touch bits 0 and 1.

Doesn’t that look so much more reasonable? :grinning: