summaryrefslogtreecommitdiff
path: root/srec.c
diff options
context:
space:
mode:
Diffstat (limited to 'srec.c')
-rw-r--r--srec.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/srec.c b/srec.c
new file mode 100644
index 0000000..dde118c
--- /dev/null
+++ b/srec.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This file is part of nios2sim-ng.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nios2sim-ng.h"
+#include "image.h"
+
+#define SREC_CRC_COUNT 1
+
+#define SREC_LINE_LENGTH 515
+static char data_buf[SREC_LINE_LENGTH];
+
+/**
+ * @return length of the line
+ */
+static ssize_t srec_read_line(FILE *fp, char *buf, size_t count)
+{
+ char c;
+ ssize_t len = 0;
+
+ while (count > 0 && !feof(fp)) {
+ if (ferror(fp)) {
+ err("ferror\n");
+ goto err_out;
+ }
+
+ /* Read byte by byte */
+ if (fread(&c, 1, 1, fp) == 1) {
+ if (c == '\n' || c == '\r')
+ break; /* end of line */
+
+ buf[len++] = c;
+ count--;
+ } else {
+ if (feof(fp) && !ferror(fp))
+ break;
+ else
+ goto err_out;
+ }
+ }
+
+ return len;
+err_out:
+ return -1;
+}
+
+static int srec_load_S3(char *buf, size_t data_count,
+ uint32_t start_addr, uint8_t *mem_base, size_t mem_size)
+{
+ size_t i;
+ uint32_t *base = (uint32_t *) mem_base;
+ off_t offset = 0;
+
+ for (i = 0; i < data_count; i += 4) {
+ uint32_t tmp;
+
+ if (sscanf(buf + i * 2, "%8x", &tmp) != 1) {
+ err("sscanf() failed on S3 record\n");
+ return -1;
+ }
+ /* SREC is Big Endian, NiosII is Little Endian */
+ tmp = bswap_32(tmp);
+
+ if (offset + i > mem_size - 1) {
+ err("Image file too large for allocated memory of %zu bytes\n", mem_size);
+ return -1;
+ }
+ /* Store in memory */
+ base[offset + i] = tmp;
+ }
+
+ return 0;
+}
+
+static int srec_handle_line(char *buf, size_t len, uint8_t *mem_base, size_t mem_size)
+{
+ unsigned int data_count = 0;
+ unsigned int start_addr;
+
+ /* Minimum valid record size */
+ if (len < 2) {
+ err("Invalid line in SREC file\n");
+ return -1;
+ }
+
+ if (buf[0] != 'S') {
+ err("Invalid line in SREC file\n");
+ return -1;
+ }
+
+ switch (buf[1]) {
+ case '0':
+ dbg("handling S0\n");
+ break;
+ case '1':
+ dbg("handling S1\n");
+ break;
+ case '2':
+ dbg("handling S2\n");
+ break;
+ case '3':
+ if (sscanf(buf, "S3%2x%8x", &data_count, &start_addr) != 2) {
+ err("Invalid S3 record\n");
+ return -1;
+ }
+
+ if (srec_load_S3(buf + 12, data_count - 4, start_addr, mem_base, mem_size) != 0)
+ return -1;
+ break;
+ case '4':
+ dbg("handling S4\n");
+ break;
+ case '5':
+ dbg("handling S5\n");
+ break;
+ case '6':
+ dbg("handling S6\n");
+ break;
+ case '7':
+ dbg("handling S7\n");
+ break;
+ case '8':
+ dbg("handling S8\n");
+ break;
+ case '9':
+ dbg("handling S9\n");
+ break;
+ default:
+ err("Invalid SREC type: %c\n", buf[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+int srec_load(FILE *fp, const char *name, uint8_t *mem_base, size_t mem_size)
+{
+ ssize_t len;
+ int ret = 0;
+
+ while (1) {
+ memset(data_buf, 0x00, SREC_LINE_LENGTH);
+ len = srec_read_line(fp, data_buf, SREC_LINE_LENGTH);
+ if (len < 0) {
+ ret = -1;
+ break;
+ } else if (len == 0) {
+ ret = 0;
+ break;
+ } else {
+ ret = srec_handle_line(data_buf, len, mem_base, mem_size);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return ret;
+}