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?
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?