include/hw/common: Fix _csr_rd_buf/_csr_wr_buf for sizeof(buf[0]) < CSR_DW_BYTES case.

- Shift in _csr_rd_buf should only been done when buf is set.
- When CSR size is not an exact multiple of the CSR data-width, the gap is in
the low addresses, not the high ones. So offset is introduced to take this into
account.
This commit is contained in:
Florent Kermarrec 2021-07-09 17:36:17 +02:00
parent b29a99cd0b
commit bc77aa37f0
1 changed files with 27 additions and 24 deletions

View File

@ -144,24 +144,26 @@ static inline void csr_wr_uint64(uint64_t v, unsigned long a)
* of the if() branches! */ * of the if() branches! */
#define _csr_rd_buf(a, buf, cnt) \ #define _csr_rd_buf(a, buf, cnt) \
{ \ { \
int i, j, nsubs, n_sub_elem; \ int i, j, offset, nsubregs, nsubelems; \
uint64_t r; \ uint64_t r; \
if (sizeof(buf[0]) >= CSR_DW_BYTES) { \ if (sizeof(buf[0]) >= CSR_DW_BYTES) { \
/* one or more subregisters per element */ \ /* One or more subregisters per element */ \
for (i = 0; i < cnt; i++) { \ for (i=0; i<cnt; i++) { \
buf[i] = _csr_rd(a, sizeof(buf[0])); \ buf[i] = _csr_rd(a, sizeof(buf[0])); \
a += CSR_OFFSET_BYTES * num_subregs(sizeof(buf[0])); \ a += CSR_OFFSET_BYTES * num_subregs(sizeof(buf[0])); \
} \ } \
} else { \ } else { \
/* multiple elements per subregister (2, 4, or 8) */ \ /* Multiple elements per subregister (2, 4, or 8) */ \
nsubs = num_subregs(sizeof(buf[0]) * cnt); \ nsubregs = num_subregs(sizeof(buf[0]) * cnt); \
n_sub_elem = CSR_DW_BYTES / sizeof(buf[0]); \ nsubelems = CSR_DW_BYTES / sizeof(buf[0]); \
for (i = 0; i < nsubs; i++) { \ offset = nsubregs*nsubelems - cnt; \
r = csr_read_simple(a); \ for (i=0; i<nsubregs; i++) { \
for (j = n_sub_elem - 1; j >= 0; j--) { \ r = csr_read_simple(a); \
if (i * n_sub_elem + j < cnt) \ for (j= nsubelems - 1; j >= 0; j--) { \
buf[i * n_sub_elem + j] = r; \ if ((i * nsubelems + j - offset) >= 0) { \
r >>= sizeof(buf[0]) * 8; \ buf[i * nsubelems + j - offset] = r; \
r >>= sizeof(buf[0]) * 8; \
} \
} \ } \
a += CSR_OFFSET_BYTES; \ a += CSR_OFFSET_BYTES; \
} \ } \
@ -175,25 +177,26 @@ static inline void csr_wr_uint64(uint64_t v, unsigned long a)
*/ */
#define _csr_wr_buf(a, buf, cnt) \ #define _csr_wr_buf(a, buf, cnt) \
{ \ { \
int i, j, nsubs, n_sub_elem; \ int i, j, offset, nsubregs, nsubelems; \
uint64_t v; \ uint64_t v; \
if (sizeof(buf[0]) >= CSR_DW_BYTES) { \ if (sizeof(buf[0]) >= CSR_DW_BYTES) { \
/* one or more subregisters per element */ \ /* One or more subregisters per element */ \
for (i = 0; i < cnt; i++) { \ for (i = 0; i < cnt; i++) { \
_csr_wr(a, buf[i], sizeof(buf[0])); \ _csr_wr(a, buf[i], sizeof(buf[0])); \
a += CSR_OFFSET_BYTES * num_subregs(sizeof(buf[0])); \ a += CSR_OFFSET_BYTES * num_subregs(sizeof(buf[0])); \
} \ } \
} else { \ } else { \
/* multiple elements per subregister (2, 4, or 8) */ \ /* Multiple elements per subregister (2, 4, or 8) */ \
nsubs = num_subregs(sizeof(buf[0]) * cnt); \ nsubregs = num_subregs(sizeof(buf[0]) * cnt); \
n_sub_elem = CSR_DW_BYTES / sizeof(buf[0]); \ nsubelems = CSR_DW_BYTES / sizeof(buf[0]); \
for (i = 0; i < nsubs; i++) { \ offset = nsubregs*nsubelems - cnt; \
v = buf[i * n_sub_elem + 0]; \ for (i = 0; i < nsubregs; i++) { \
for (j = 1; j < n_sub_elem; j++) { \ v = 0; \
if (i * n_sub_elem + j == cnt) \ for (j= 0; j < nsubelems; j++) { \
break; \ if ((i * nsubelems + j - offset) >= 0) { \
v <<= sizeof(buf[0]) * 8; \ v <<= sizeof(buf[0]) * 8; \
v |= buf[i * n_sub_elem + j]; \ v |= buf[i * nsubelems + j - offset]; \
} \
} \ } \
csr_write_simple(v, a); \ csr_write_simple(v, a); \
a += CSR_OFFSET_BYTES; \ a += CSR_OFFSET_BYTES; \