/********************************************************************** ** cmv_snap.c ** Trigger a CMV12K Image Capture and Dump the Data ** Version 1.1 ** ** Copyright (C) 2013 H.Poetzl ** ** This program is free software: you can redistribute it and/or ** modify it under the terms of the GNU General Public License ** as published by the Free Software Foundation, either version ** 2 of the License, or (at your option) any later version. ** **********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "V1.1" static char *cmd_name = NULL; static uint32_t reg_base = 0x60000000; static uint32_t reg_size = 0x00020000; static uint32_t reg_addr = 0x00000000; static uint32_t buf_base = 0x1B000000; static uint32_t buf_size = 0x02000000; static uint32_t buf_addr = 0x00000000; static char *dev_mem = "/dev/mem"; static uint16_t pattern[4] = { 0x000, 0x555, 0xFFF, 0xAAA }; static uint32_t exp_time = 0x600; static uint16_t exp_reg85 = 0x101; static int32_t out_lsl = 4; static bool out_16 = true; static bool opt_bcols = false; static bool opt_dumpr = false; #define CLOCK_ID CLOCK_REALTIME typedef long long int (stoll_t)(const char *, char **, int); long long int argtoll( const char *str, const char **end, stoll_t stoll) { int bit, inv = 0; long long int val = 0; char *eptr; if (!str) return -1; if (!stoll) stoll = strtoll; switch (*str) { case '~': case '!': inv = 1; /* invert */ str++; default: break; } while (*str) { switch (*str) { case '^': bit = strtol(str+1, &eptr, 0); val ^= (1LL << bit); break; case '&': val &= stoll(str+1, &eptr, 0); break; case '|': val |= stoll(str+1, &eptr, 0); break; case '-': case '+': case ',': case '=': break; default: val = stoll(str, &eptr, 0); break; } if (eptr == str) break; str = eptr; } if (end) *end = eptr; return (inv)?~(val):(val); } static inline void write_value(uint16_t val) { val = (val & 0xFFF) << out_lsl; if (out_16) putchar(val & 0xFF); putchar(val >> 8); } static inline void write_range(uint16_t *a, uint16_t *b) { for (uint16_t *ptr = a; ptr < b; ptr++) write_value(*ptr); } static inline void write_block(uint16_t *blk) { write_range(blk, blk + 16); } static inline void write_line(uint16_t *ptr, unsigned channels, unsigned pixels) { for (int c=0; c> 8); } static inline double ns_to_us(double val) { return val * 1e-3; } static inline double ns_to_ms(double val) { return val * 1e-6; } static inline double len_to_ns(uint32_t len) { double lvds = 300e6; double bits = 12; double channels = 16; return len * (bits/lvds) * 1e9 / channels; } static inline double len_to_us(uint32_t len) { return ns_to_us(len_to_ns(len)); } static inline double len_to_ms(uint32_t len) { return ns_to_ms(len_to_ns(len)); } static inline double delta_ns(struct timespec *a, struct timespec *b) { double delta = b->tv_nsec - a->tv_nsec; return delta + (b->tv_sec - a->tv_sec) * 1e9; } static inline double delta_us(struct timespec *a, struct timespec *b) { return ns_to_us(delta_ns(a, b)); } static inline double delta_ms(struct timespec *a, struct timespec *b) { return ns_to_ms(delta_ns(a, b)); } static inline double exposure(uint32_t time, uint16_t reg85) { double lvds = 300e6; double bits = 12; return ((time - 1)*(reg85 + 1) + 1701) * (bits/lvds) * 1e9; } void parse_pattern(const char *ptr, uint16_t *pattern) { const char *sep = NULL; pattern[0] = argtoll(ptr, &sep, strtoll) & 0xFFF; if (sep[0] == ',') { pattern[1] = argtoll(&sep[1], &sep, strtoll) & 0xFFF; if (sep[0] == ',') { pattern[2] = argtoll(&sep[1], &sep, strtoll) & 0xFFF; if (sep[0] == ',') { pattern[3] = argtoll(&sep[1], &sep, strtoll) & 0xFFF; } } } fprintf(stderr, "pattern 0x%03X,0x%03X,0x%03X,0x%03X\n", pattern[0], pattern[1], pattern[2], pattern[3]); } void parse_exposure(const char *ptr, uint32_t *time, uint16_t *reg85) { const char *sep = NULL; *time = argtoll(ptr, &sep, strtoll) & 0xFFFFFF; if (sep[0] == ',') { *reg85 = argtoll(&sep[1], &sep, strtoll) & 0xFFFF; } fprintf(stderr, "exposure 0x%06X,0x%04X [%4.3fms]\n", *time, *reg85, ns_to_ms(exposure(*time, *reg85))); } uint16_t get_cmv_reg(unsigned reg) { volatile uint32_t *ptr = (uint32_t *)0x60000000; return ptr[reg] & 0xFFFF; } void set_cmv_reg(unsigned reg, uint16_t val) { volatile uint32_t *ptr = (uint32_t *)0x60000000; ptr[reg] = val; usleep(100); } uint32_t get_fil_reg(unsigned reg) { volatile uint32_t *ptr = (uint32_t *)0x60010000; return ptr[reg]; } void set_fil_reg(unsigned reg, uint32_t val) { volatile uint32_t *ptr = (uint32_t *)0x60010000; ptr[reg] = val; usleep(100); } uint32_t get_del_reg(unsigned reg) { volatile uint32_t *ptr = (uint32_t *)0x60011000; return ptr[reg]; } void set_del_reg(unsigned reg, uint32_t val) { volatile uint32_t *ptr = (uint32_t *)0x60011000; ptr[reg] = val; usleep(100); } #define BLK_FOT 0x8000 #define BLK_FVAL 0x4000 #define BLK_LVAL 0x2000 #define BLK_DVAL 0x1000 #define VAL_MASK 0x7000 typedef enum _block { BLK_UNKNOWN = 0x0, BLK_IDLE = 0x1, BLK_IDLE_F = 0x1 | BLK_FVAL, BLK_IDLE_FL = 0x1 | BLK_FVAL | BLK_LVAL, BLK_OH = 0x2, BLK_OH_F = 0x2 | BLK_FVAL, BLK_OH_FL = 0x2 | BLK_FVAL | BLK_LVAL, BLK_DATA = 0x4 | BLK_FVAL | BLK_LVAL | BLK_DVAL } block_t; block_t classify(uint16_t *blk) { uint8_t slot[8] = { 0 }; uint8_t pat[2] = { 0 }; for (int i=0; i<16; i++) { slot[(blk[i] >> 12) & 0x7]++; if ((blk[i] & 0xFFF) == pattern[3]) pat[0]++; if ((blk[i] & 0xFFF) == (~pattern[3] & 0xFF)) pat[1]++; } if (pat[0] > 12) { /* idle */ if (slot[0x0] > 12) return BLK_IDLE; else if (slot[0x4] > 12) return BLK_IDLE_F; else if (slot[0x6] > 12) return BLK_IDLE_FL; } else if (pat[1] > 12) { /* OH */ if (slot[0x0] > 12) return BLK_OH; else if (slot[0x4] > 12) return BLK_OH_F; else if (slot[0x6] > 12) return BLK_OH_FL; } if (slot[0x7] > 12) return BLK_DATA; return BLK_UNKNOWN; } int main(int argc, char *argv[]) { extern int optind; extern char *optarg; int c, err_flag = 0; cmd_name = argv[0]; while ((c = getopt(argc, argv, "h8brs:e:B:S:A:M:Z:P:")) != EOF) { switch (c) { case 'h': fprintf(stderr, "This is %s " VERSION "\n" "options are:\n" "-h print this help message\n" "-8 output 8 bit per pixel\n" "-b enable black columns\n" "-r dump sensor registers\n" "-e exposure values\n" "-s shift values by \n" "-B register mapping base\n" "-S register mapping size\n" "-A register mapping address\n" "-M buffer memory base\n" "-Z buffer memory size\n" "-P mark pattern\n" , cmd_name); exit(0); break; case '8': out_16 = false; break; case 'b': opt_bcols = true; break; case 'r': opt_dumpr = true; break; case 's': out_lsl = argtoll(optarg, NULL, NULL); break; case 'e': parse_exposure(optarg, &exp_time, &exp_reg85); break; case 'B': reg_base = argtoll(optarg, NULL, NULL); break; case 'S': reg_size = argtoll(optarg, NULL, NULL); break; case 'A': reg_addr = argtoll(optarg, NULL, NULL); break; case 'M': buf_addr = argtoll(optarg, NULL, NULL); break; case 'Z': buf_size = argtoll(optarg, NULL, NULL); break; case 'P': parse_pattern(optarg, pattern); break; case '?': default: err_flag++; break; } } if (err_flag) { fprintf(stderr, "Usage: %s -[hvB:S:E:] path ...\n" "%s -h for help.\n", cmd_name, cmd_name); exit(2); } int fd = open(dev_mem, O_RDWR | O_SYNC); if (fd == -1) { fprintf(stderr, "error opening >%s<.\n%s\n", dev_mem, strerror(errno)); exit(1); } if (reg_addr == 0) reg_addr = reg_base; void *base = mmap((void *)reg_addr, reg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, reg_base); if (base == (void *)-1) { fprintf(stderr, "error mapping 0x%08lX+0x%08lX @0x%08lX.\n%s\n", (long)reg_base, (long)reg_size, (long)reg_addr, strerror(errno)); exit(2); } fprintf(stderr, "mapped 0x%08lX+0x%08lX to 0x%08lX.\n", (long unsigned)reg_base, (long unsigned)reg_size, (long unsigned)base); void *buf = mmap((void *)buf_addr, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf_base); if (buf == (void *)-1) { fprintf(stderr, "error mapping 0x%08lX+0x%08lX @0x%08lX.\n%s\n", (long)buf_base, (long)buf_size, (long)buf_addr, strerror(errno)); exit(2); } fprintf(stderr, "mapped 0x%08lX+0x%08lX to 0x%08lX.\n", (long unsigned)buf_base, (long unsigned)buf_size, (long unsigned)buf); fprintf(stderr, "priming buffer memory ...\n"); for (int a=0; a> 16); clock_gettime(CLOCK_ID, &pat_time[0]); set_cmv_reg(89, pattern[0] | bcols); /* 'zero' pattern */ set_fil_reg(1, 0x00000000); /* disable overrides */ set_fil_reg(2, 0x000303FF); /* reset combiners */ set_fil_reg(3, 0x00330303); /* reset addr/data gen */ usleep(1000); set_fil_reg(3, 0x00330300); /* disable writers */ clock_gettime(CLOCK_ID, &req_time); set_fil_reg(1, 0x10100000); /* override frame req */ if (exp_us > 4000) usleep(exp_us - 4000); set_fil_reg(3, 0x00000003); /* enable gen/writers */ set_fil_reg(2, 0x00000000); /* enable combiners */ // set_fil_reg(1, 0x10100000); /* override frame req */ clock_gettime(CLOCK_ID, &cap_time); set_fil_reg(2, 0x10000000); /* enable capture */ // usleep(1000); clock_gettime(CLOCK_ID, &pat_time[1]); set_cmv_reg(89, pattern[1] | bcols); /* first pattern */ clock_gettime(CLOCK_ID, &pat_time[2]); set_cmv_reg(89, pattern[2] | bcols); /* second pattern */ clock_gettime(CLOCK_ID, &pat_time[3]); set_cmv_reg(89, pattern[3] | bcols); /* third pattern */ set_fil_reg(1, 0x00000000); /* disable frame req */ usleep(30000); set_fil_reg(2, 0x00000000); /* disable capture */ set_fil_reg(3, 0x00000000); /* disable writers */ uint16_t *pat_pos[2][4] = { { NULL } }; uint16_t *fot_pos[2][2] = { { NULL } }; uint32_t buf_half = buf_size/2; uint16_t *s[2] = { buf, buf + buf_half }; uint16_t *e[2] = { buf + buf_half, buf + buf_size }; uint16_t *p; uint32_t align = 0; uint16_t *row_a[2][4096] = { { NULL } }; uint16_t *row_b[2][4096] = { { NULL } }; uint16_t *row_c[2][4096] = { { NULL } }; uint16_t *row_d[2][4096] = { { NULL } }; uint32_t row_cnt[2] = { 0, 0 }; for (int b=0; b<2; b++) { p = s[b]; for (int i=0; i<4; i++) { while ((p < e[b]) && (*p != pattern[i])) p++; if (p == e[b]) { fprintf(stderr, "buf[%u]: pattern %u[0x%03X] not found.\n", b, i, pattern[i]); } else { pat_pos[b][i] = p; fprintf(stderr, "buf[%u]: pattern %u[0x%03X] found at 0x%06X.\n", b, i, pattern[i], (p - s[b])); } } while ((p < e[b]) && ((*p & 0x8000) == 0x0000)) p++; fot_pos[b][0] = p; if (p == e[b]) { fprintf(stderr, "buf[%u]: FOT not found.\n", b); } else { fprintf(stderr, "buf[%u]: FOT found at 0x%06X.\n", b, (p - s[b])); } while ((p < e[b]) && ((*p & 0x8000) == 0x8000)) p++; fot_pos[b][1] = p; fprintf(stderr, "buf[%u]: FOT length is 0x%04X [%3.2fus].\n", b, (p - fot_pos[b][0]), len_to_us(p - fot_pos[b][0])); align = (fot_pos[b][0] - s[b]) & (fot_pos[b][1] - s[b]) & 0xF; if ((((pat_pos[b][1] -s[b]) & 0xF) == align) && (((pat_pos[b][2] -s[b]) & 0xF) == align) && (((pat_pos[b][3] -s[b]) & 0xF) == align)) { fprintf(stderr, "buf[%u]: alignment fixed to 0x%X.\n", b, align); } p = s[b] + (((p - s[b]) & ~0xF) | align); uint32_t prev_val = 0; uint32_t this_row = 0; bool frame_done = false; for (;p < e[b]; p+=16) { block_t bt = classify(p); /* printf("%c", ((bt & 0x7) | ((bt & 0x7000) >> 9)) + 64); */ switch (bt) { case BLK_UNKNOWN: break; case BLK_IDLE: if (prev_val == 0x7000) /* data */ frame_done = true; if (prev_val == 0x7000) /* data */ row_d[b][this_row] = p; break; case BLK_IDLE_F: case BLK_IDLE_FL: if (prev_val == 0x7000) /* data */ row_b[b][this_row] = p; break; case BLK_OH: if (prev_val == 0x7000) /* data */ frame_done = true; case BLK_OH_F: case BLK_OH_FL: if (prev_val == 0x7000) /* data */ row_d[b][this_row] = p; break; case BLK_DATA: if (prev_val == 0x6000) { row_c[b][this_row] = p; row_d[b][this_row] = p; } else if ((prev_val == 0x4000) || (prev_val == 0x0000)) { this_row = row_cnt[b]++; row_a[b][this_row] = p; row_b[b][this_row] = p; } // block_out(p); break; } prev_val = bt & VAL_MASK; if (frame_done) break; } } double cap_delta = delta_ns(&req_time, &cap_time); double fot_delta = len_to_ns(fot_pos[0][0] - s[0]); fprintf(stderr, "timing (relative to frame request):\n" " %10.3fms exposure\n" " @%10.3fms capture\n", ns_to_ms(cap_delta + fot_delta), ns_to_ms(cap_delta)); for (int i=1; i<4; i++) fprintf(stderr, " @%10.3fms pattern %u ~[%3.2fus]\n", delta_ms(&req_time, &pat_time[i]), i, len_to_us(pat_pos[0][i] - pat_pos[0][i - 1])); /* redundant info */ fprintf(stderr, " @%10.3fms FOT ~[%3.2fus]\n", ns_to_ms(cap_delta + fot_delta), len_to_us(fot_pos[0][1] - fot_pos[0][0])); fprintf(stderr, "row data (%u):\n", row_cnt[0] + row_cnt[1]); for (int i=0; i