Arnaud Minier | 214652d | 2024-03-29 18:44:02 +0100 | [diff] [blame] | 1 | /* |
| 2 | * QTest testcase for STML4X5_USART |
| 3 | * |
| 4 | * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr> |
| 5 | * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr> |
| 6 | * |
| 7 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 8 | * See the COPYING file in the top-level directory. |
| 9 | */ |
| 10 | |
| 11 | #include "qemu/osdep.h" |
| 12 | #include "libqtest.h" |
| 13 | #include "hw/misc/stm32l4x5_rcc_internals.h" |
| 14 | #include "hw/registerfields.h" |
Inès Varhol | 88446cf | 2024-10-14 17:05:52 +0100 | [diff] [blame] | 15 | #include "stm32l4x5.h" |
Arnaud Minier | 214652d | 2024-03-29 18:44:02 +0100 | [diff] [blame] | 16 | |
| 17 | #define RCC_BASE_ADDR 0x40021000 |
| 18 | /* Use USART 1 ADDR, assume the others work the same */ |
| 19 | #define USART1_BASE_ADDR 0x40013800 |
| 20 | |
| 21 | /* See stm32l4x5_usart for definitions */ |
| 22 | REG32(CR1, 0x00) |
| 23 | FIELD(CR1, M1, 28, 1) |
| 24 | FIELD(CR1, OVER8, 15, 1) |
| 25 | FIELD(CR1, M0, 12, 1) |
| 26 | FIELD(CR1, PCE, 10, 1) |
| 27 | FIELD(CR1, TXEIE, 7, 1) |
| 28 | FIELD(CR1, RXNEIE, 5, 1) |
| 29 | FIELD(CR1, TE, 3, 1) |
| 30 | FIELD(CR1, RE, 2, 1) |
| 31 | FIELD(CR1, UE, 0, 1) |
| 32 | REG32(CR2, 0x04) |
| 33 | REG32(CR3, 0x08) |
| 34 | FIELD(CR3, OVRDIS, 12, 1) |
| 35 | REG32(BRR, 0x0C) |
| 36 | REG32(GTPR, 0x10) |
| 37 | REG32(RTOR, 0x14) |
| 38 | REG32(RQR, 0x18) |
| 39 | REG32(ISR, 0x1C) |
Jacob Abrams | 6cce0dc | 2024-09-10 21:32:55 -0700 | [diff] [blame] | 40 | FIELD(ISR, REACK, 22, 1) |
| 41 | FIELD(ISR, TEACK, 21, 1) |
Arnaud Minier | 214652d | 2024-03-29 18:44:02 +0100 | [diff] [blame] | 42 | FIELD(ISR, TXE, 7, 1) |
| 43 | FIELD(ISR, RXNE, 5, 1) |
| 44 | FIELD(ISR, ORE, 3, 1) |
| 45 | REG32(ICR, 0x20) |
| 46 | REG32(RDR, 0x24) |
| 47 | REG32(TDR, 0x28) |
| 48 | |
| 49 | #define NVIC_ISPR1 0XE000E204 |
| 50 | #define NVIC_ICPR1 0xE000E284 |
| 51 | #define USART1_IRQ 37 |
| 52 | |
| 53 | static bool check_nvic_pending(QTestState *qts, unsigned int n) |
| 54 | { |
| 55 | /* No USART interrupts are less than 32 */ |
| 56 | assert(n > 32); |
| 57 | n -= 32; |
| 58 | return qtest_readl(qts, NVIC_ISPR1) & (1 << n); |
| 59 | } |
| 60 | |
| 61 | static bool clear_nvic_pending(QTestState *qts, unsigned int n) |
| 62 | { |
| 63 | /* No USART interrupts are less than 32 */ |
| 64 | assert(n > 32); |
| 65 | n -= 32; |
| 66 | qtest_writel(qts, NVIC_ICPR1, (1 << n)); |
| 67 | return true; |
| 68 | } |
| 69 | |
| 70 | /* |
| 71 | * Wait indefinitely for the flag to be updated. |
| 72 | * If this is run on a slow CI runner, |
| 73 | * the meson harness will timeout after 10 minutes for us. |
| 74 | */ |
| 75 | static bool usart_wait_for_flag(QTestState *qts, uint32_t event_addr, |
| 76 | uint32_t flag) |
| 77 | { |
| 78 | while (true) { |
| 79 | if ((qtest_readl(qts, event_addr) & flag)) { |
| 80 | return true; |
| 81 | } |
| 82 | g_usleep(1000); |
| 83 | } |
| 84 | |
| 85 | return false; |
| 86 | } |
| 87 | |
| 88 | static void usart_receive_string(QTestState *qts, int sock_fd, const char *in, |
| 89 | char *out) |
| 90 | { |
| 91 | int i, in_len = strlen(in); |
| 92 | |
| 93 | g_assert_true(send(sock_fd, in, in_len, 0) == in_len); |
| 94 | for (i = 0; i < in_len; i++) { |
| 95 | g_assert_true(usart_wait_for_flag(qts, |
| 96 | USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK)); |
| 97 | out[i] = qtest_readl(qts, USART1_BASE_ADDR + A_RDR); |
| 98 | } |
| 99 | out[i] = '\0'; |
| 100 | } |
| 101 | |
| 102 | static void usart_send_string(QTestState *qts, const char *in) |
| 103 | { |
| 104 | int i, in_len = strlen(in); |
| 105 | |
| 106 | for (i = 0; i < in_len; i++) { |
| 107 | qtest_writel(qts, USART1_BASE_ADDR + A_TDR, in[i]); |
| 108 | g_assert_true(usart_wait_for_flag(qts, |
| 109 | USART1_BASE_ADDR + A_ISR, R_ISR_TXE_MASK)); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | /* Init the RCC clocks to run at 80 MHz */ |
| 114 | static void init_clocks(QTestState *qts) |
| 115 | { |
| 116 | uint32_t value; |
| 117 | |
| 118 | /* MSIRANGE can be set only when MSI is OFF or READY */ |
| 119 | qtest_writel(qts, (RCC_BASE_ADDR + A_CR), R_CR_MSION_MASK); |
| 120 | |
| 121 | /* Clocking from MSI, in case MSI was not the default source */ |
| 122 | qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); |
| 123 | |
| 124 | /* |
| 125 | * Update PLL and set MSI as the source clock. |
| 126 | * PLLM = 1 --> 000 |
| 127 | * PLLN = 40 --> 40 |
| 128 | * PPLLR = 2 --> 00 |
| 129 | * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1) |
| 130 | * SRC = MSI --> 01 |
| 131 | */ |
| 132 | qtest_writel(qts, (RCC_BASE_ADDR + A_PLLCFGR), R_PLLCFGR_PLLREN_MASK | |
| 133 | (40 << R_PLLCFGR_PLLN_SHIFT) | |
| 134 | (0b01 << R_PLLCFGR_PLLSRC_SHIFT)); |
| 135 | |
| 136 | /* PLL activation */ |
| 137 | |
| 138 | value = qtest_readl(qts, (RCC_BASE_ADDR + A_CR)); |
| 139 | qtest_writel(qts, (RCC_BASE_ADDR + A_CR), value | R_CR_PLLON_MASK); |
| 140 | |
| 141 | /* RCC_CFGR is OK by defaut */ |
| 142 | qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); |
| 143 | |
| 144 | /* CCIPR : no periph clock by default */ |
| 145 | qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); |
| 146 | |
| 147 | /* Switches on the PLL clock source */ |
| 148 | value = qtest_readl(qts, (RCC_BASE_ADDR + A_CFGR)); |
| 149 | qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), (value & ~R_CFGR_SW_MASK) | |
| 150 | (0b11 << R_CFGR_SW_SHIFT)); |
| 151 | |
| 152 | /* Enable SYSCFG clock enabled */ |
| 153 | qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), R_APB2ENR_SYSCFGEN_MASK); |
| 154 | |
| 155 | /* Enable the IO port B clock (See p.252) */ |
| 156 | qtest_writel(qts, (RCC_BASE_ADDR + A_AHB2ENR), R_AHB2ENR_GPIOBEN_MASK); |
| 157 | |
| 158 | /* Enable the clock for USART1 (cf p.259) */ |
| 159 | /* We rewrite SYSCFGEN to not disable it */ |
| 160 | qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), |
| 161 | R_APB2ENR_SYSCFGEN_MASK | R_APB2ENR_USART1EN_MASK); |
| 162 | |
| 163 | /* TODO: Enable usart via gpio */ |
| 164 | |
| 165 | /* Set PCLK as the clock for USART1(cf p.272) i.e. reset both bits */ |
| 166 | qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); |
| 167 | |
| 168 | /* Reset USART1 (see p.249) */ |
| 169 | qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 1 << 14); |
| 170 | qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 0); |
| 171 | } |
| 172 | |
| 173 | static void init_uart(QTestState *qts) |
| 174 | { |
| 175 | uint32_t cr1; |
| 176 | |
| 177 | init_clocks(qts); |
| 178 | |
| 179 | /* |
| 180 | * For 115200 bauds, see p.1349. |
| 181 | * The clock has a frequency of 80Mhz, |
| 182 | * for 115200, we have to put a divider of 695 = 0x2B7. |
| 183 | */ |
| 184 | qtest_writel(qts, (USART1_BASE_ADDR + A_BRR), 0x2B7); |
| 185 | |
| 186 | /* |
| 187 | * Set the oversampling by 16, |
| 188 | * disable the parity control and |
| 189 | * set the word length to 8. (cf p.1377) |
| 190 | */ |
| 191 | cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); |
| 192 | cr1 &= ~(R_CR1_M1_MASK | R_CR1_M0_MASK | R_CR1_OVER8_MASK | R_CR1_PCE_MASK); |
| 193 | qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), cr1); |
| 194 | |
| 195 | /* Enable the transmitter, the receiver and the USART. */ |
| 196 | qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), |
Jacob Abrams | 6cce0dc | 2024-09-10 21:32:55 -0700 | [diff] [blame] | 197 | cr1 | R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); |
Arnaud Minier | 214652d | 2024-03-29 18:44:02 +0100 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | static void test_write_read(void) |
| 201 | { |
| 202 | QTestState *qts = qtest_init("-M b-l475e-iot01a"); |
| 203 | |
| 204 | /* Test that we can write and retrieve a value from the device */ |
| 205 | qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 0xFFFFFFFF); |
| 206 | const uint32_t tdr = qtest_readl(qts, USART1_BASE_ADDR + A_TDR); |
| 207 | g_assert_cmpuint(tdr, ==, 0x000001FF); |
Peter Maydell | d1e8bea | 2024-09-05 17:55:53 +0100 | [diff] [blame] | 208 | |
| 209 | qtest_quit(qts); |
Arnaud Minier | 214652d | 2024-03-29 18:44:02 +0100 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | static void test_receive_char(void) |
| 213 | { |
| 214 | int sock_fd; |
| 215 | uint32_t cr1; |
| 216 | QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); |
| 217 | |
| 218 | init_uart(qts); |
| 219 | |
| 220 | /* Try without initializing IRQ */ |
| 221 | g_assert_true(send(sock_fd, "a", 1, 0) == 1); |
| 222 | usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); |
| 223 | g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'a'); |
| 224 | g_assert_false(check_nvic_pending(qts, USART1_IRQ)); |
| 225 | |
| 226 | /* Now with the IRQ */ |
| 227 | cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); |
| 228 | cr1 |= R_CR1_RXNEIE_MASK; |
| 229 | qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); |
| 230 | g_assert_true(send(sock_fd, "b", 1, 0) == 1); |
| 231 | usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); |
| 232 | g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'b'); |
| 233 | g_assert_true(check_nvic_pending(qts, USART1_IRQ)); |
| 234 | clear_nvic_pending(qts, USART1_IRQ); |
| 235 | |
| 236 | close(sock_fd); |
| 237 | |
| 238 | qtest_quit(qts); |
| 239 | } |
| 240 | |
| 241 | static void test_send_char(void) |
| 242 | { |
| 243 | int sock_fd; |
| 244 | char s[1]; |
| 245 | uint32_t cr1; |
| 246 | QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); |
| 247 | |
| 248 | init_uart(qts); |
| 249 | |
| 250 | /* Try without initializing IRQ */ |
| 251 | qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'c'); |
| 252 | g_assert_true(recv(sock_fd, s, 1, 0) == 1); |
| 253 | g_assert_cmphex(s[0], ==, 'c'); |
| 254 | g_assert_false(check_nvic_pending(qts, USART1_IRQ)); |
| 255 | |
| 256 | /* Now with the IRQ */ |
| 257 | cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); |
| 258 | cr1 |= R_CR1_TXEIE_MASK; |
| 259 | qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); |
| 260 | qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'd'); |
| 261 | g_assert_true(recv(sock_fd, s, 1, 0) == 1); |
| 262 | g_assert_cmphex(s[0], ==, 'd'); |
| 263 | g_assert_true(check_nvic_pending(qts, USART1_IRQ)); |
| 264 | clear_nvic_pending(qts, USART1_IRQ); |
| 265 | |
| 266 | close(sock_fd); |
| 267 | |
| 268 | qtest_quit(qts); |
| 269 | } |
| 270 | |
| 271 | static void test_receive_str(void) |
| 272 | { |
| 273 | int sock_fd; |
| 274 | char s[10]; |
| 275 | QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); |
| 276 | |
| 277 | init_uart(qts); |
| 278 | |
| 279 | usart_receive_string(qts, sock_fd, "hello", s); |
| 280 | g_assert_true(memcmp(s, "hello", 5) == 0); |
| 281 | |
| 282 | close(sock_fd); |
| 283 | |
| 284 | qtest_quit(qts); |
| 285 | } |
| 286 | |
| 287 | static void test_send_str(void) |
| 288 | { |
| 289 | int sock_fd; |
| 290 | char s[10]; |
| 291 | QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); |
| 292 | |
| 293 | init_uart(qts); |
| 294 | |
| 295 | usart_send_string(qts, "world"); |
| 296 | g_assert_true(recv(sock_fd, s, 10, 0) == 5); |
| 297 | g_assert_true(memcmp(s, "world", 5) == 0); |
| 298 | |
| 299 | close(sock_fd); |
| 300 | |
| 301 | qtest_quit(qts); |
| 302 | } |
| 303 | |
Jacob Abrams | 6cce0dc | 2024-09-10 21:32:55 -0700 | [diff] [blame] | 304 | static void test_ack(void) |
| 305 | { |
| 306 | uint32_t cr1; |
| 307 | uint32_t isr; |
| 308 | QTestState *qts = qtest_init("-M b-l475e-iot01a"); |
| 309 | |
| 310 | init_uart(qts); |
| 311 | |
| 312 | cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); |
| 313 | |
| 314 | /* Disable the transmitter and receiver. */ |
| 315 | qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), |
| 316 | cr1 & ~(R_CR1_RE_MASK | R_CR1_TE_MASK)); |
| 317 | |
| 318 | /* Test ISR ACK for transmitter and receiver disabled */ |
| 319 | isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); |
| 320 | g_assert_false(isr & R_ISR_TEACK_MASK); |
| 321 | g_assert_false(isr & R_ISR_REACK_MASK); |
| 322 | |
| 323 | /* Enable the transmitter and receiver. */ |
| 324 | qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), |
| 325 | cr1 | (R_CR1_RE_MASK | R_CR1_TE_MASK)); |
| 326 | |
| 327 | /* Test ISR ACK for transmitter and receiver disabled */ |
| 328 | isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); |
| 329 | g_assert_true(isr & R_ISR_TEACK_MASK); |
| 330 | g_assert_true(isr & R_ISR_REACK_MASK); |
| 331 | |
| 332 | qtest_quit(qts); |
| 333 | } |
| 334 | |
Inès Varhol | 88446cf | 2024-10-14 17:05:52 +0100 | [diff] [blame] | 335 | static void check_clock(QTestState *qts, const char *path, uint32_t rcc_reg, |
| 336 | uint32_t reg_offset) |
| 337 | { |
| 338 | g_assert_cmpuint(get_clock_period(qts, path), ==, 0); |
| 339 | qtest_writel(qts, rcc_reg, qtest_readl(qts, rcc_reg) | (0x1 << reg_offset)); |
| 340 | g_assert_cmpuint(get_clock_period(qts, path), ==, SYSCLK_PERIOD); |
| 341 | } |
| 342 | |
| 343 | static void test_clock_enable(void) |
| 344 | { |
| 345 | /* |
| 346 | * For each USART device, enable its clock in RCC |
| 347 | * and check that its clock frequency is SYSCLK_PERIOD |
| 348 | */ |
| 349 | QTestState *qts = qtest_init("-M b-l475e-iot01a"); |
| 350 | |
| 351 | check_clock(qts, "machine/soc/usart[0]/clk", RCC_APB2ENR, 14); |
| 352 | check_clock(qts, "machine/soc/usart[1]/clk", RCC_APB1ENR1, 17); |
| 353 | check_clock(qts, "machine/soc/usart[2]/clk", RCC_APB1ENR1, 18); |
| 354 | check_clock(qts, "machine/soc/uart[0]/clk", RCC_APB1ENR1, 19); |
| 355 | check_clock(qts, "machine/soc/uart[1]/clk", RCC_APB1ENR1, 20); |
| 356 | check_clock(qts, "machine/soc/lpuart1/clk", RCC_APB1ENR2, 0); |
| 357 | |
| 358 | qtest_quit(qts); |
| 359 | } |
| 360 | |
Arnaud Minier | 214652d | 2024-03-29 18:44:02 +0100 | [diff] [blame] | 361 | int main(int argc, char **argv) |
| 362 | { |
| 363 | int ret; |
| 364 | |
| 365 | g_test_init(&argc, &argv, NULL); |
| 366 | g_test_set_nonfatal_assertions(); |
| 367 | |
| 368 | qtest_add_func("stm32l4x5/usart/write_read", test_write_read); |
| 369 | qtest_add_func("stm32l4x5/usart/receive_char", test_receive_char); |
| 370 | qtest_add_func("stm32l4x5/usart/send_char", test_send_char); |
| 371 | qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str); |
| 372 | qtest_add_func("stm32l4x5/usart/send_str", test_send_str); |
Jacob Abrams | 6cce0dc | 2024-09-10 21:32:55 -0700 | [diff] [blame] | 373 | qtest_add_func("stm32l4x5/usart/ack", test_ack); |
Inès Varhol | 88446cf | 2024-10-14 17:05:52 +0100 | [diff] [blame] | 374 | qtest_add_func("stm32l4x5/usart/clock_enable", test_clock_enable); |
Arnaud Minier | 214652d | 2024-03-29 18:44:02 +0100 | [diff] [blame] | 375 | ret = g_test_run(); |
| 376 | |
| 377 | return ret; |
| 378 | } |
| 379 | |