| 1 | /* |
| 2 | * QTest testcase for Microbit board using the Nordic Semiconductor nRF51 SoC. |
| 3 | * |
| 4 | * nRF51: |
| 5 | * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf |
| 6 | * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf |
| 7 | * |
| 8 | * Microbit Board: http://microbit.org/ |
| 9 | * |
| 10 | * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de> |
| 11 | * |
| 12 | * This code is licensed under the GPL version 2 or later. See |
| 13 | * the COPYING file in the top-level directory. |
| 14 | */ |
| 15 | |
| 16 | |
| 17 | #include "qemu/osdep.h" |
| 18 | #include "exec/hwaddr.h" |
| 19 | #include "libqtest.h" |
| 20 | |
| 21 | #include "hw/arm/nrf51.h" |
| 22 | #include "hw/char/nrf51_uart.h" |
| 23 | #include "hw/gpio/nrf51_gpio.h" |
| 24 | #include "hw/nvram/nrf51_nvm.h" |
| 25 | #include "hw/timer/nrf51_timer.h" |
| 26 | #include "hw/i2c/microbit_i2c.h" |
| 27 | |
| 28 | static bool uart_wait_for_event(QTestState *qts, uint32_t event_addr) |
| 29 | { |
| 30 | time_t now, start = time(NULL); |
| 31 | |
| 32 | while (true) { |
| 33 | if (qtest_readl(qts, event_addr) == 1) { |
| 34 | qtest_writel(qts, event_addr, 0x00); |
| 35 | return true; |
| 36 | } |
| 37 | |
| 38 | /* Wait at most 10 minutes */ |
| 39 | now = time(NULL); |
| 40 | if (now - start > 600) { |
| 41 | break; |
| 42 | } |
| 43 | g_usleep(10000); |
| 44 | } |
| 45 | |
| 46 | return false; |
| 47 | } |
| 48 | |
| 49 | static void uart_rw_to_rxd(QTestState *qts, int sock_fd, const char *in, |
| 50 | char *out) |
| 51 | { |
| 52 | int i, in_len = strlen(in); |
| 53 | |
| 54 | g_assert_true(send(sock_fd, in, in_len, 0) == in_len); |
| 55 | for (i = 0; i < in_len; i++) { |
| 56 | g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + |
| 57 | A_UART_RXDRDY)); |
| 58 | out[i] = qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD); |
| 59 | } |
| 60 | out[i] = '\0'; |
| 61 | } |
| 62 | |
| 63 | static void uart_w_to_txd(QTestState *qts, const char *in) |
| 64 | { |
| 65 | int i, in_len = strlen(in); |
| 66 | |
| 67 | for (i = 0; i < in_len; i++) { |
| 68 | qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, in[i]); |
| 69 | g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + |
| 70 | A_UART_TXDRDY)); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | static void test_nrf51_uart(void) |
| 75 | { |
| 76 | int sock_fd; |
| 77 | char s[10]; |
| 78 | QTestState *qts = qtest_init_with_serial("-M microbit", &sock_fd); |
| 79 | |
| 80 | g_assert_true(send(sock_fd, "c", 1, 0) == 1); |
| 81 | g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 0x00); |
| 82 | |
| 83 | qtest_writel(qts, NRF51_UART_BASE + A_UART_ENABLE, 0x04); |
| 84 | qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTRX, 0x01); |
| 85 | |
| 86 | g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + A_UART_RXDRDY)); |
| 87 | qtest_writel(qts, NRF51_UART_BASE + A_UART_RXDRDY, 0x00); |
| 88 | g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 'c'); |
| 89 | |
| 90 | qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENSET, 0x04); |
| 91 | g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x04); |
| 92 | qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENCLR, 0x04); |
| 93 | g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x00); |
| 94 | |
| 95 | uart_rw_to_rxd(qts, sock_fd, "hello", s); |
| 96 | g_assert_true(memcmp(s, "hello", 5) == 0); |
| 97 | |
| 98 | qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); |
| 99 | uart_w_to_txd(qts, "d"); |
| 100 | g_assert_true(recv(sock_fd, s, 10, 0) == 1); |
| 101 | g_assert_cmphex(s[0], ==, 'd'); |
| 102 | |
| 103 | qtest_writel(qts, NRF51_UART_BASE + A_UART_SUSPEND, 0x01); |
| 104 | qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, 'h'); |
| 105 | qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); |
| 106 | uart_w_to_txd(qts, "world"); |
| 107 | g_assert_true(recv(sock_fd, s, 10, 0) == 5); |
| 108 | g_assert_true(memcmp(s, "world", 5) == 0); |
| 109 | |
| 110 | close(sock_fd); |
| 111 | |
| 112 | qtest_quit(qts); |
| 113 | } |
| 114 | |
| 115 | /* Read a byte from I2C device at @addr from register @reg */ |
| 116 | static uint32_t i2c_read_byte(QTestState *qts, uint32_t addr, uint32_t reg) |
| 117 | { |
| 118 | uint32_t val; |
| 119 | |
| 120 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ADDRESS, addr); |
| 121 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTTX, 1); |
| 122 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_TXD, reg); |
| 123 | val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_TXDSENT); |
| 124 | g_assert_cmpuint(val, ==, 1); |
| 125 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); |
| 126 | |
| 127 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTRX, 1); |
| 128 | val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_RXDREADY); |
| 129 | g_assert_cmpuint(val, ==, 1); |
| 130 | val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_REG_RXD); |
| 131 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); |
| 132 | |
| 133 | return val; |
| 134 | } |
| 135 | |
| 136 | static void test_microbit_i2c(void) |
| 137 | { |
| 138 | uint32_t val; |
| 139 | QTestState *qts = qtest_init("-M microbit"); |
| 140 | |
| 141 | /* We don't program pins/irqs but at least enable the device */ |
| 142 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 5); |
| 143 | |
| 144 | /* MMA8653 magnetometer detection */ |
| 145 | val = i2c_read_byte(qts, 0x3A, 0x0D); |
| 146 | g_assert_cmphex(val, ==, 0x5A); |
| 147 | |
| 148 | val = i2c_read_byte(qts, 0x3A, 0x0D); |
| 149 | g_assert_cmphex(val, ==, 0x5A); |
| 150 | |
| 151 | /* LSM303 accelerometer detection */ |
| 152 | val = i2c_read_byte(qts, 0x3C, 0x4F); |
| 153 | g_assert_cmphex(val, ==, 0x40); |
| 154 | |
| 155 | qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0); |
| 156 | |
| 157 | qtest_quit(qts); |
| 158 | } |
| 159 | |
| 160 | #define FLASH_SIZE (256 * NRF51_PAGE_SIZE) |
| 161 | |
| 162 | static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size, |
| 163 | uint32_t address_reg) |
| 164 | { |
| 165 | hwaddr i; |
| 166 | |
| 167 | /* Erase Page */ |
| 168 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); |
| 169 | qtest_writel(qts, NRF51_NVMC_BASE + address_reg, base); |
| 170 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 171 | |
| 172 | /* Check memory */ |
| 173 | for (i = 0; i < size / 4; i++) { |
| 174 | g_assert_cmphex(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); |
| 175 | } |
| 176 | |
| 177 | /* Fill memory */ |
| 178 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); |
| 179 | for (i = 0; i < size / 4; i++) { |
| 180 | qtest_writel(qts, base + i * 4, i); |
| 181 | g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, i); |
| 182 | } |
| 183 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 184 | } |
| 185 | |
| 186 | static void test_nrf51_nvmc(void) |
| 187 | { |
| 188 | uint32_t value; |
| 189 | hwaddr i; |
| 190 | QTestState *qts = qtest_init("-M microbit"); |
| 191 | |
| 192 | /* Test always ready */ |
| 193 | value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY); |
| 194 | g_assert_cmphex(value & 0x01, ==, 0x01); |
| 195 | |
| 196 | /* Test write-read config register */ |
| 197 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03); |
| 198 | g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), |
| 199 | ==, 0x03); |
| 200 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 201 | g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), |
| 202 | ==, 0x00); |
| 203 | |
| 204 | /* Test PCR0 */ |
| 205 | fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, |
| 206 | NRF51_NVMC_ERASEPCR0); |
| 207 | fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, |
| 208 | NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR0); |
| 209 | |
| 210 | /* Test PCR1 */ |
| 211 | fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, |
| 212 | NRF51_NVMC_ERASEPCR1); |
| 213 | fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, |
| 214 | NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR1); |
| 215 | |
| 216 | /* Erase all */ |
| 217 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); |
| 218 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); |
| 219 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 220 | |
| 221 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); |
| 222 | for (i = 0; i < FLASH_SIZE / 4; i++) { |
| 223 | qtest_writel(qts, NRF51_FLASH_BASE + i * 4, i); |
| 224 | g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), ==, i); |
| 225 | } |
| 226 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 227 | |
| 228 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); |
| 229 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); |
| 230 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 231 | |
| 232 | for (i = 0; i < FLASH_SIZE / 4; i++) { |
| 233 | g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), |
| 234 | ==, 0xFFFFFFFF); |
| 235 | } |
| 236 | |
| 237 | /* Erase UICR */ |
| 238 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); |
| 239 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); |
| 240 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 241 | |
| 242 | for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { |
| 243 | g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), |
| 244 | ==, 0xFFFFFFFF); |
| 245 | } |
| 246 | |
| 247 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); |
| 248 | for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { |
| 249 | qtest_writel(qts, NRF51_UICR_BASE + i * 4, i); |
| 250 | g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), ==, i); |
| 251 | } |
| 252 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 253 | |
| 254 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); |
| 255 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); |
| 256 | qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); |
| 257 | |
| 258 | for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { |
| 259 | g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), |
| 260 | ==, 0xFFFFFFFF); |
| 261 | } |
| 262 | |
| 263 | qtest_quit(qts); |
| 264 | } |
| 265 | |
| 266 | static void test_nrf51_gpio(void) |
| 267 | { |
| 268 | size_t i; |
| 269 | uint32_t actual, expected; |
| 270 | |
| 271 | struct { |
| 272 | hwaddr addr; |
| 273 | uint32_t expected; |
| 274 | } const reset_state[] = { |
| 275 | {NRF51_GPIO_REG_OUT, 0x00000000}, {NRF51_GPIO_REG_OUTSET, 0x00000000}, |
| 276 | {NRF51_GPIO_REG_OUTCLR, 0x00000000}, {NRF51_GPIO_REG_IN, 0x00000000}, |
| 277 | {NRF51_GPIO_REG_DIR, 0x00000000}, {NRF51_GPIO_REG_DIRSET, 0x00000000}, |
| 278 | {NRF51_GPIO_REG_DIRCLR, 0x00000000} |
| 279 | }; |
| 280 | |
| 281 | QTestState *qts = qtest_init("-M microbit"); |
| 282 | |
| 283 | /* Check reset state */ |
| 284 | for (i = 0; i < ARRAY_SIZE(reset_state); i++) { |
| 285 | expected = reset_state[i].expected; |
| 286 | actual = qtest_readl(qts, NRF51_GPIO_BASE + reset_state[i].addr); |
| 287 | g_assert_cmpuint(actual, ==, expected); |
| 288 | } |
| 289 | |
| 290 | for (i = 0; i < NRF51_GPIO_PINS; i++) { |
| 291 | expected = 0x00000002; |
| 292 | actual = qtest_readl(qts, NRF51_GPIO_BASE + |
| 293 | NRF51_GPIO_REG_CNF_START + i * 4); |
| 294 | g_assert_cmpuint(actual, ==, expected); |
| 295 | } |
| 296 | |
| 297 | /* Check dir bit consistency between dir and cnf */ |
| 298 | /* Check set via DIRSET */ |
| 299 | expected = 0x80000001; |
| 300 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected); |
| 301 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); |
| 302 | g_assert_cmpuint(actual, ==, expected); |
| 303 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) |
| 304 | & 0x01; |
| 305 | g_assert_cmphex(actual, ==, 0x01); |
| 306 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; |
| 307 | g_assert_cmphex(actual, ==, 0x01); |
| 308 | |
| 309 | /* Check clear via DIRCLR */ |
| 310 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001); |
| 311 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); |
| 312 | g_assert_cmphex(actual, ==, 0x00000000); |
| 313 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) |
| 314 | & 0x01; |
| 315 | g_assert_cmphex(actual, ==, 0x00); |
| 316 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; |
| 317 | g_assert_cmphex(actual, ==, 0x00); |
| 318 | |
| 319 | /* Check set via DIR */ |
| 320 | expected = 0x80000001; |
| 321 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected); |
| 322 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); |
| 323 | g_assert_cmpuint(actual, ==, expected); |
| 324 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) |
| 325 | & 0x01; |
| 326 | g_assert_cmphex(actual, ==, 0x01); |
| 327 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; |
| 328 | g_assert_cmphex(actual, ==, 0x01); |
| 329 | |
| 330 | /* Reset DIR */ |
| 331 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000); |
| 332 | |
| 333 | /* Check Input propagates */ |
| 334 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00); |
| 335 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); |
| 336 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 337 | g_assert_cmphex(actual, ==, 0x00); |
| 338 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); |
| 339 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 340 | g_assert_cmphex(actual, ==, 0x01); |
| 341 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); |
| 342 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 343 | g_assert_cmphex(actual, ==, 0x01); |
| 344 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); |
| 345 | |
| 346 | /* Check pull-up working */ |
| 347 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); |
| 348 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); |
| 349 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 350 | g_assert_cmphex(actual, ==, 0x00); |
| 351 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110); |
| 352 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 353 | g_assert_cmphex(actual, ==, 0x01); |
| 354 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); |
| 355 | |
| 356 | /* Check pull-down working */ |
| 357 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); |
| 358 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); |
| 359 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 360 | g_assert_cmphex(actual, ==, 0x01); |
| 361 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110); |
| 362 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 363 | g_assert_cmphex(actual, ==, 0x00); |
| 364 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); |
| 365 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); |
| 366 | |
| 367 | /* Check Output propagates */ |
| 368 | qtest_irq_intercept_out(qts, "/machine/nrf51"); |
| 369 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011); |
| 370 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); |
| 371 | g_assert_true(qtest_get_irq(qts, 0)); |
| 372 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); |
| 373 | g_assert_false(qtest_get_irq(qts, 0)); |
| 374 | |
| 375 | /* Check self-stimulation */ |
| 376 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); |
| 377 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); |
| 378 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 379 | g_assert_cmphex(actual, ==, 0x01); |
| 380 | |
| 381 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); |
| 382 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; |
| 383 | g_assert_cmphex(actual, ==, 0x00); |
| 384 | |
| 385 | /* |
| 386 | * Check short-circuit - generates an guest_error which must be checked |
| 387 | * manually as long as qtest can not scan qemu_log messages |
| 388 | */ |
| 389 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); |
| 390 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); |
| 391 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); |
| 392 | |
| 393 | qtest_quit(qts); |
| 394 | } |
| 395 | |
| 396 | static void test_nrf51_gpio_detect(void) |
| 397 | { |
| 398 | QTestState *qts = qtest_init("-M microbit"); |
| 399 | int i; |
| 400 | |
| 401 | /* Connect input buffer on pins 1-7, configure SENSE for high level */ |
| 402 | for (i = 1; i <= 7; i++) { |
| 403 | qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START + i * 4, |
| 404 | deposit32(0, 16, 2, 2)); |
| 405 | } |
| 406 | |
| 407 | qtest_irq_intercept_out_named(qts, "/machine/nrf51/gpio", "detect"); |
| 408 | |
| 409 | for (i = 1; i <= 7; i++) { |
| 410 | /* Set pin high */ |
| 411 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 1); |
| 412 | uint32_t actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); |
| 413 | g_assert_cmphex(actual, ==, 1 << i); |
| 414 | |
| 415 | /* Check that DETECT is high */ |
| 416 | g_assert_true(qtest_get_irq(qts, 0)); |
| 417 | |
| 418 | /* Set pin low, check that DETECT goes low. */ |
| 419 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 0); |
| 420 | actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); |
| 421 | g_assert_cmphex(actual, ==, 0x0); |
| 422 | g_assert_false(qtest_get_irq(qts, 0)); |
| 423 | } |
| 424 | |
| 425 | /* Set pin 0 high, check that DETECT doesn't fire */ |
| 426 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); |
| 427 | g_assert_false(qtest_get_irq(qts, 0)); |
| 428 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); |
| 429 | |
| 430 | /* Set pins 1, 2, and 3 high, then set 3 low. Check DETECT is still high */ |
| 431 | for (i = 1; i <= 3; i++) { |
| 432 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 1); |
| 433 | } |
| 434 | g_assert_true(qtest_get_irq(qts, 0)); |
| 435 | qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 3, 0); |
| 436 | g_assert_true(qtest_get_irq(qts, 0)); |
| 437 | |
| 438 | qtest_quit(qts); |
| 439 | } |
| 440 | |
| 441 | static void timer_task(QTestState *qts, hwaddr task) |
| 442 | { |
| 443 | qtest_writel(qts, NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK); |
| 444 | } |
| 445 | |
| 446 | static void timer_clear_event(QTestState *qts, hwaddr event) |
| 447 | { |
| 448 | qtest_writel(qts, NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR); |
| 449 | } |
| 450 | |
| 451 | static void timer_set_bitmode(QTestState *qts, uint8_t mode) |
| 452 | { |
| 453 | qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode); |
| 454 | } |
| 455 | |
| 456 | static void timer_set_prescaler(QTestState *qts, uint8_t prescaler) |
| 457 | { |
| 458 | qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler); |
| 459 | } |
| 460 | |
| 461 | static void timer_set_cc(QTestState *qts, size_t idx, uint32_t value) |
| 462 | { |
| 463 | qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value); |
| 464 | } |
| 465 | |
| 466 | static void timer_assert_events(QTestState *qts, uint32_t ev0, uint32_t ev1, |
| 467 | uint32_t ev2, uint32_t ev3) |
| 468 | { |
| 469 | g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0) |
| 470 | == ev0); |
| 471 | g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1) |
| 472 | == ev1); |
| 473 | g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2) |
| 474 | == ev2); |
| 475 | g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3) |
| 476 | == ev3); |
| 477 | } |
| 478 | |
| 479 | static void test_nrf51_timer(void) |
| 480 | { |
| 481 | uint32_t steps_to_overflow = 408; |
| 482 | QTestState *qts = qtest_init("-M microbit"); |
| 483 | |
| 484 | /* Compare Match */ |
| 485 | timer_task(qts, NRF51_TIMER_TASK_STOP); |
| 486 | timer_task(qts, NRF51_TIMER_TASK_CLEAR); |
| 487 | |
| 488 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); |
| 489 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); |
| 490 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); |
| 491 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); |
| 492 | |
| 493 | timer_set_bitmode(qts, NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */ |
| 494 | timer_set_prescaler(qts, 0); |
| 495 | /* Swept over, during the first step */ |
| 496 | timer_set_cc(qts, 0, 2); |
| 497 | /* Barely miss, after the second step */ |
| 498 | timer_set_cc(qts, 1, 162); |
| 499 | /* Spot on, after the third step */ |
| 500 | timer_set_cc(qts, 2, 480); |
| 501 | |
| 502 | timer_assert_events(qts, 0, 0, 0, 0); |
| 503 | |
| 504 | timer_task(qts, NRF51_TIMER_TASK_START); |
| 505 | qtest_clock_step(qts, 10000); |
| 506 | timer_assert_events(qts, 1, 0, 0, 0); |
| 507 | |
| 508 | /* Swept over on first overflow */ |
| 509 | timer_set_cc(qts, 3, 114); |
| 510 | |
| 511 | qtest_clock_step(qts, 10000); |
| 512 | timer_assert_events(qts, 1, 1, 0, 0); |
| 513 | |
| 514 | qtest_clock_step(qts, 10000); |
| 515 | timer_assert_events(qts, 1, 1, 1, 0); |
| 516 | |
| 517 | /* Wrap time until internal counter overflows */ |
| 518 | while (steps_to_overflow--) { |
| 519 | timer_assert_events(qts, 1, 1, 1, 0); |
| 520 | qtest_clock_step(qts, 10000); |
| 521 | } |
| 522 | |
| 523 | timer_assert_events(qts, 1, 1, 1, 1); |
| 524 | |
| 525 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); |
| 526 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); |
| 527 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); |
| 528 | timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); |
| 529 | timer_assert_events(qts, 0, 0, 0, 0); |
| 530 | |
| 531 | timer_task(qts, NRF51_TIMER_TASK_STOP); |
| 532 | |
| 533 | /* Test Proposal: Stop/Shutdown */ |
| 534 | /* Test Proposal: Shortcut Compare -> Clear */ |
| 535 | /* Test Proposal: Shortcut Compare -> Stop */ |
| 536 | /* Test Proposal: Counter Mode */ |
| 537 | |
| 538 | qtest_quit(qts); |
| 539 | } |
| 540 | |
| 541 | int main(int argc, char **argv) |
| 542 | { |
| 543 | g_test_init(&argc, &argv, NULL); |
| 544 | |
| 545 | qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart); |
| 546 | qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio); |
| 547 | qtest_add_func("/microbit/nrf51/gpio_detect", test_nrf51_gpio_detect); |
| 548 | qtest_add_func("/microbit/nrf51/nvmc", test_nrf51_nvmc); |
| 549 | qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer); |
| 550 | qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c); |
| 551 | |
| 552 | return g_test_run(); |
| 553 | } |