aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Selkirk <paul@psgd.org>2019-04-03 17:41:10 -0400
committerPaul Selkirk <paul@psgd.org>2019-04-03 17:41:10 -0400
commita590fe12485603003101b5a4ba2f616083d040f4 (patch)
tree36819a49c479e2d4d37005902ab2f87a2015839a
parent9006c25bd73c00ff861cccbce4595e6c932f4ace (diff)
parent624527539a83dbfd0dc2f03fda1cff14a1669811 (diff)
Merge branch 'fmc_clk_60mhz' to 'master'
-rw-r--r--projects/board-test/fmc-perf.c20
-rw-r--r--projects/board-test/fmc-probe.c5
-rw-r--r--projects/board-test/fmc-test.c16
-rw-r--r--projects/cli-test/test-fmc.c12
-rw-r--r--stm-fmc.c39
-rw-r--r--stm-fmc.h52
6 files changed, 51 insertions, 93 deletions
diff --git a/projects/board-test/fmc-perf.c b/projects/board-test/fmc-perf.c
index 71d0149..5af0946 100644
--- a/projects/board-test/fmc-perf.c
+++ b/projects/board-test/fmc-perf.c
@@ -31,14 +31,8 @@ static void sanity(void)
uint32_t rnd, data;
rnd = random();
- if (fmc_write_32(0, rnd) != 0) {
- uart_send_string("fmc_write_32 failed\r\n");
- Error_Handler();
- }
- if (fmc_read_32(0, &data) != 0) {
- uart_send_string("fmc_read_32 failed\r\n");
- Error_Handler();
- }
+ fmc_write_32(0, rnd);
+ fmc_read_32(0, &data);
if (data != rnd) {
uart_send_string("Data bus fail: expected ");
uart_send_hex(rnd, 8);
@@ -76,10 +70,7 @@ static void test_read(void)
uint32_t i, data;
for (i = 0; i < TEST_NUM_ROUNDS; ++i) {
- if (fmc_read_32(0, &data) != 0) {
- uart_send_string("fmc_read_32 failed\r\n");
- Error_Handler();
- }
+ fmc_read_32(0, &data);
}
}
@@ -88,10 +79,7 @@ static void test_write(void)
uint32_t i;
for (i = 0; i < TEST_NUM_ROUNDS; ++i) {
- if (fmc_write_32(0, i) != 0) {
- uart_send_string("fmc_write_32 failed\r\n");
- Error_Handler();
- }
+ fmc_write_32(0, i);
}
}
diff --git a/projects/board-test/fmc-probe.c b/projects/board-test/fmc-probe.c
index 5f7fdb5..38897ab 100644
--- a/projects/board-test/fmc-probe.c
+++ b/projects/board-test/fmc-probe.c
@@ -21,10 +21,7 @@ static uint32_t read0(uint32_t addr)
{
uint32_t data;
- if (fmc_read_32(addr, &data) != 0) {
- uart_send_string("fmc_read_32 failed\r\n");
- Error_Handler();
- }
+ fmc_read_32(addr, &data);
return data;
}
diff --git a/projects/board-test/fmc-test.c b/projects/board-test/fmc-test.c
index 1421db0..bd30dd5 100644
--- a/projects/board-test/fmc-test.c
+++ b/projects/board-test/fmc-test.c
@@ -158,7 +158,7 @@ int main(void)
int test_fpga_data_bus(void)
//------------------------------------------------------------------------------
{
- int c, ok;
+ int c;
uint32_t rnd, buf;
HAL_StatusTypeDef hal_result;
@@ -171,12 +171,10 @@ int test_fpga_data_bus(void)
if (hal_result != HAL_OK) break;
// write value to fpga at address 0
- ok = fmc_write_32(0, rnd);
- if (ok != 0) break;
+ fmc_write_32(0, rnd);
// read value from fpga
- ok = fmc_read_32(0, &buf);
- if (ok != 0) break;
+ fmc_read_32(0, &buf);
// compare (abort testing in case of error)
if (buf != rnd)
@@ -218,7 +216,7 @@ int test_fpga_data_bus(void)
int test_fpga_address_bus(void)
//------------------------------------------------------------------------------
{
- int c, ok;
+ int c;
uint32_t rnd, buf;
HAL_StatusTypeDef hal_result;
@@ -239,12 +237,10 @@ int test_fpga_address_bus(void)
if (rnd == 0) continue;
// write dummy value to fpga at some non-zero address
- ok = fmc_write_32(rnd, buf);
- if (ok != 0) break;
+ fmc_write_32(rnd, buf);
// read value from fpga
- ok = fmc_read_32(0, &buf);
- if (ok != 0) break;
+ fmc_read_32(0, &buf);
// fpga receives address of 32-bit word, while we need
// byte address here to compare
diff --git a/projects/cli-test/test-fmc.c b/projects/cli-test/test-fmc.c
index 6773cfc..d9b0c9b 100644
--- a/projects/cli-test/test-fmc.c
+++ b/projects/cli-test/test-fmc.c
@@ -80,16 +80,8 @@ static int _write_then_read(struct cli_def *cli, uint32_t addr, uint32_t write_b
{
int ok;
- ok = fmc_write_32(addr, write_buf);
- if (ok != 0) {
- cli_print(cli, "FMC write failed: 0x%x", ok);
- return 0;
- }
- ok = fmc_read_32(0, read_buf);
- if (ok != 0) {
- cli_print(cli, "FMC read failed: 0x%x", ok);
- return 0;
- }
+ fmc_write_32(addr, write_buf);
+ fmc_read_32(0, read_buf);
return 1;
}
diff --git a/stm-fmc.c b/stm-fmc.c
index c5086b4..1019531 100644
--- a/stm-fmc.c
+++ b/stm-fmc.c
@@ -115,7 +115,7 @@ void fmc_init(void)
_fmc_fpga_inst.Init.WrapMode = FMC_WRAP_MODE_DISABLE;
// don't care in fixed latency mode
- _fmc_fpga_inst.Init.WaitSignalActive = FMC_WAIT_TIMING_DURING_WS;
+ _fmc_fpga_inst.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
// allow write access to fpga
_fmc_fpga_inst.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
@@ -153,14 +153,43 @@ void fmc_init(void)
fmc_timing.BusTurnAroundDuration = 0;
// use smallest allowed divisor for best performance
- fmc_timing.CLKDivision = 2;
-
- // stm is too slow to work with min allowed 2-cycle latency
- fmc_timing.DataLatency = 3;
+ //
+ // FMC_CLK = HCLK / CLKDivision, HCLK is 180 MHz
+ //
+ // Allowed values for CLKDivision are integers >= 2.
+ //
+ // Division == 2: FMC_CLK = 180 / 2 = 90 MHz (highest allowed frequency)
+ // Division == 3: FMC_CLK = 180 / 3 = 60 MHz (one step below)
+ // ...
+ //
+
+// fmc_timing.CLKDivision = 2; // 90 MHz
+ fmc_timing.CLKDivision = 3; // 60 MHz
+
+ // use min suitable for fastest transfer
+ fmc_timing.DataLatency = 4;
// don't care in sync mode
fmc_timing.AccessMode = FMC_ACCESS_MODE_A;
// initialize fmc
HAL_SRAM_Init(&_fmc_fpga_inst, &fmc_timing, NULL);
+
+ // STM32 only enables FMC clock right before the very first read/write
+ // access. FPGA takes certain time (<= 100 us) to lock its PLL to this frequency,
+ // so a certain number of initial FMC transactions may be missed. One read transaction
+ // takes ~0.1 us (9 ticks @ 90 MHz), so doing 1000 dummy reads will make sure, that FPGA
+ // has already locked its PLL and is ready. Another way around is to repeatedly read
+ // some register that is guaranteed to have known value until reading starts returning
+ // correct data.
+
+ // to prevent compiler from optimizing this away, we pretent we're calculating sum
+ int cyc;
+ uint32_t sum;
+ volatile uint32_t part;
+
+ for (cyc = 0; cyc < 1000; cyc++) {
+ part = *(__IO uint32_t *)FMC_FPGA_BASE_ADDR;
+ sum += part;
+ }
}
diff --git a/stm-fmc.h b/stm-fmc.h
index eab053d..92b261b 100644
--- a/stm-fmc.h
+++ b/stm-fmc.h
@@ -39,12 +39,6 @@
#define FMC_FPGA_BASE_ADDR 0x60000000
#define FMC_FPGA_ADDR_MASK 0x03FFFFFC // there are 26 physical lines, but "only" 24 usable for now
-#define FMC_FPGA_NWAIT_MAX_POLL_TICKS 10
-
-#define FMC_GPIO_PORT_NWAIT GPIOD
-#define FMC_GPIO_PIN_NWAIT GPIO_PIN_6
-
-#define FMC_NWAIT_IDLE GPIO_PIN_SET
#define fmc_af_gpio(port, pins) \
GPIO_InitStruct.Pin = pins; \
@@ -58,60 +52,22 @@
extern void fmc_init(void);
-static inline HAL_StatusTypeDef _fmc_nwait_idle(void)
-{
- int cnt;
-
- // poll NWAIT (number of iterations is limited)
- for (cnt=0; cnt<FMC_FPGA_NWAIT_MAX_POLL_TICKS; cnt++)
- {
- // read pin state
- if (HAL_GPIO_ReadPin(FMC_GPIO_PORT_NWAIT, FMC_GPIO_PIN_NWAIT) == FMC_NWAIT_IDLE)
- return HAL_OK;
- }
-
- return HAL_ERROR;
-}
-
-static inline HAL_StatusTypeDef fmc_write_32(const uint32_t addr, const uint32_t data)
+static inline void fmc_write_32(const uint32_t addr, const uint32_t data)
{
// calculate target fpga address
uint32_t *ptr = (uint32_t *) (FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK));
// write data to fpga
*ptr = data;
-
- // wait for transaction to complete
- return _fmc_nwait_idle();
}
-static inline HAL_StatusTypeDef fmc_read_32(const uint32_t addr, uint32_t * const data)
+static inline void fmc_read_32(const uint32_t addr, uint32_t * const data)
{
// calculate target fpga address
uint32_t *ptr = (uint32_t *) (FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK));
- /* Pavel says:
- * The short story is like, on one hand STM32 has a dedicated FMC_NWAIT
- * pin, that can be used in variable-latency data transfer mode. On the
- * other hand STM32 also has a very nasty hardware bug associated with
- * FMC_WAIT, that causes processor to freeze under certain conditions.
- * Because of this FMC_NWAIT cannot be used and FPGA can't properly signal
- * to STM32, when data transfer is done. Because of that we have to read
- * two times.
- */
-
- HAL_StatusTypeDef status;
-
- *data = *ptr;
- status = _fmc_nwait_idle();
-
- if (status != HAL_OK)
- return status;
-
- *data = *ptr;
- status = _fmc_nwait_idle();
-
- return status;
+ // read data from fpga
+ *data = *ptr;
}
#endif /* __STM_FMC_H */