Task 098: .COE File Format

Task 098: .COE File Format

1. List of Properties of the .COE File Format Intrinsic to Its File System

The .COE file format is a text-based format primarily used by Xilinx (now AMD) tools for initializing memory contents in FPGA designs. It is not a binary format tied to specific file system attributes like allocation units or metadata beyond standard text file characteristics. The intrinsic properties refer to the format's structural elements, which are parsed from the text content. Based on the specifications, the key properties are:

  • Memory Initialization Radix: An integer value specifying the numerical base for the data values in the memory initialization vector. Supported values are 2 (binary), 8 (octal), 10 (decimal), or 16 (hexadecimal). This property determines how the data values are interpreted.
  • Memory Initialization Vector: A comma-separated list of data values expressed in the specified radix. These values represent the memory contents and can span multiple lines. The list is terminated by a semicolon. The number of values implies the memory depth, and the bit width is inferred from the values themselves. Comments in the file begin with a semicolon and are ignored during parsing.

These properties are the core elements of the format, with optional comments for documentation.

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .COE File Parsing

The following is an HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post. It creates a drag-and-drop area for a .COE file, parses the file upon drop, extracts the radix and vector properties, decodes the vector values to integers, and displays them on the screen.

Drag and drop a .COE file here

4. Python Class for .COE File Handling

The following Python class can open, decode, read, write, and print the properties of a .COE file to the console.

import re

class COEFile:
    def __init__(self):
        self.radix = None
        self.vector = []  # List of integers

    def load(self, filename):
        with open(filename, 'r') as f:
            text = f.read()
        # Remove comments
        text = re.sub(r';.*?(?=\n|$)', '', text)
        # Parse radix
        radix_match = re.search(r'memory_initialization_radix\s*=\s*(\d+)', text, re.IGNORECASE)
        if radix_match:
            self.radix = int(radix_match.group(1))
        # Parse vector
        vector_match = re.search(r'memory_initialization_vector\s*=\s*(.*)', text, re.IGNORECASE | re.DOTALL)
        if vector_match:
            vector_str = vector_match.group(1).replace(';', '').strip()
            values = [v.strip() for v in vector_str.split(',') if v.strip()]
            self.vector = [int(v, self.radix) for v in values]

    def print_properties(self):
        if self.radix is None:
            print("No properties loaded.")
            return
        print(f"Memory Initialization Radix: {self.radix}")
        print("Memory Initialization Vector (decoded to integers):")
        print(', '.join(map(str, self.vector)))

    def write(self, filename, radix, vector):
        self.radix = radix
        self.vector = vector
        with open(filename, 'w') as f:
            f.write("; Generated .COE file\n")
            f.write(f"memory_initialization_radix = {radix};\n")
            f.write("memory_initialization_vector =\n")
            if radix == 2:
                values = [bin(v)[2:] for v in vector]
            elif radix == 8:
                values = [oct(v)[2:] for v in vector]
            elif radix == 10:
                values = [str(v) for v in vector]
            elif radix == 16:
                values = [hex(v)[2:].lower() for v in vector]
            else:
                raise ValueError("Unsupported radix")
            f.write(',\n'.join(values) + ';\n')

# Example usage:
# coe = COEFile()
# coe.load('input.coe')
# coe.print_properties()
# coe.write('output.coe', 16, [0, 1, 2, 3])

5. Java Class for .COE File Handling

The following Java class can open, decode, read, write, and print the properties of a .COE file to the console.

import java.io.*;
import java.util.*;
import java.util.regex.*;

public class COEFile {
    private Integer radix;
    private List<Integer> vector = new ArrayList<>();

    public void load(String filename) throws IOException {
        StringBuilder text = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = br.readLine()) != null) {
                text.append(line).append("\n");
            }
        }
        // Remove comments
        String cleaned = text.toString().replaceAll(";.*?(?=\\n|$)", "");
        // Parse radix
        Pattern radixPattern = Pattern.compile("memory_initialization_radix\\s*=\\s*(\\d+)", Pattern.CASE_INSENSITIVE);
        Matcher radixMatcher = radixPattern.matcher(cleaned);
        if (radixMatcher.find()) {
            radix = Integer.parseInt(radixMatcher.group(1));
        }
        // Parse vector
        Pattern vectorPattern = Pattern.compile("memory_initialization_vector\\s*=\\s*(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher vectorMatcher = vectorPattern.matcher(cleaned);
        if (vectorMatcher.find()) {
            String vectorStr = vectorMatcher.group(1).replace(";", "").trim();
            String[] values = vectorStr.split(",");
            for (String v : values) {
                String trimmed = v.trim();
                if (!trimmed.isEmpty()) {
                    vector.add(Integer.parseInt(trimmed, radix));
                }
            }
        }
    }

    public void printProperties() {
        if (radix == null) {
            System.out.println("No properties loaded.");
            return;
        }
        System.out.println("Memory Initialization Radix: " + radix);
        System.out.println("Memory Initialization Vector (decoded to integers):");
        System.out.println(String.join(", ", vector.stream().map(String::valueOf).toArray(String[]::new)));
    }

    public void write(String filename, int radix, List<Integer> vector) throws IOException {
        this.radix = radix;
        this.vector = vector;
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(filename))) {
            bw.write("; Generated .COE file\n");
            bw.write("memory_initialization_radix = " + radix + ";\n");
            bw.write("memory_initialization_vector =\n");
            List<String> values = new ArrayList<>();
            for (int v : vector) {
                if (radix == 2) {
                    values.add(Integer.toBinaryString(v));
                } else if (radix == 8) {
                    values.add(Integer.toOctalString(v));
                } else if (radix == 10) {
                    values.add(String.valueOf(v));
                } else if (radix == 16) {
                    values.add(Integer.toHexString(v).toLowerCase());
                } else {
                    throw new IllegalArgumentException("Unsupported radix");
                }
            }
            bw.write(String.join(",\n", values) + ";\n");
        }
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     COEFile coe = new COEFile();
    //     coe.load("input.coe");
    //     coe.printProperties();
    //     coe.write("output.coe", 16, Arrays.asList(0, 1, 2, 3));
    // }
}

6. JavaScript Class for .COE File Handling

The following JavaScript class is designed for Node.js (requires fs module) and can open, decode, read, write, and print the properties of a .COE file to the console.

const fs = require('fs');

class COEFile {
    constructor() {
        this.radix = null;
        this.vector = []; // Array of numbers
    }

    load(filename) {
        const text = fs.readFileSync(filename, 'utf8');
        // Remove comments
        const cleaned = text.replace(/;.*?(?=\n|$)/g, '');
        // Parse radix
        const radixMatch = cleaned.match(/memory_initialization_radix\s*=\s*(\d+)/i);
        if (radixMatch) {
            this.radix = parseInt(radixMatch[1], 10);
        }
        // Parse vector
        const vectorMatch = cleaned.match(/memory_initialization_vector\s*=\s*(.*)/is);
        if (vectorMatch) {
            const vectorStr = vectorMatch[1].replace(';', '').trim();
            const values = vectorStr.split(',').map(v => v.trim()).filter(v => v);
            this.vector = values.map(v => parseInt(v, this.radix));
        }
    }

    printProperties() {
        if (this.radix === null) {
            console.log('No properties loaded.');
            return;
        }
        console.log(`Memory Initialization Radix: ${this.radix}`);
        console.log('Memory Initialization Vector (decoded to integers):');
        console.log(this.vector.join(', '));
    }

    write(filename, radix, vector) {
        this.radix = radix;
        this.vector = vector;
        let values = vector.map(v => {
            if (radix === 2) return v.toString(2);
            if (radix === 8) return v.toString(8);
            if (radix === 10) return v.toString(10);
            if (radix === 16) return v.toString(16).toLowerCase();
            throw new Error('Unsupported radix');
        });
        const content = `; Generated .COE file\nmemory_initialization_radix = ${radix};\nmemory_initialization_vector =\n${values.join(',\n')};\n`;
        fs.writeFileSync(filename, content);
    }
}

// Example usage:
// const coe = new COEFile();
// coe.load('input.coe');
// coe.printProperties();
// coe.write('output.coe', 16, [0, 1, 2, 3]);

7. C Implementation for .COE File Handling

Since C does not support classes natively, the following implementation uses a struct with associated functions to open, decode, read, write, and print the properties of a .COE file to the console.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINE 1024
#define MAX_VALUES 10000  // Arbitrary max for vector size

typedef struct {
    int radix;
    int* vector;
    size_t vector_size;
} COEFile;

COEFile* coe_create() {
    COEFile* coe = malloc(sizeof(COEFile));
    coe->radix = -1;
    coe->vector = NULL;
    coe->vector_size = 0;
    return coe;
}

void coe_destroy(COEFile* coe) {
    free(coe->vector);
    free(coe);
}

int coe_load(COEFile* coe, const char* filename) {
    FILE* fp = fopen(filename, "r");
    if (!fp) return -1;
    
    char line[MAX_LINE];
    char text[1024 * 1024] = {0};  // Arbitrary large buffer
    while (fgets(line, sizeof(line), fp)) {
        strcat(text, line);
    }
    fclose(fp);
    
    // Remove comments
    char* p = text;
    while ((p = strchr(p, ';')) != NULL) {
        while (*p != '\n' && *p != '\0') *p++ = ' ';
    }
    
    // Parse radix
    char* radix_str = strstr(text, "memory_initialization_radix");
    if (radix_str) {
        radix_str = strchr(radix_str, '=');
        if (radix_str) coe->radix = atoi(radix_str + 1);
    }
    
    // Parse vector
    char* vector_str = strstr(text, "memory_initialization_vector");
    if (vector_str) {
        vector_str = strchr(vector_str, '=');
        if (vector_str) {
            vector_str++;
            coe->vector = malloc(sizeof(int) * MAX_VALUES);
            char* token = strtok(vector_str, ",;");
            size_t i = 0;
            while (token && i < MAX_VALUES) {
                while (isspace(*token)) token++;
                if (*token) {
                    coe->vector[i++] = (int)strtol(token, NULL, coe->radix);
                }
                token = strtok(NULL, ",;");
            }
            coe->vector_size = i;
        }
    }
    return 0;
}

void coe_print_properties(const COEFile* coe) {
    if (coe->radix == -1) {
        printf("No properties loaded.\n");
        return;
    }
    printf("Memory Initialization Radix: %d\n", coe->radix);
    printf("Memory Initialization Vector (decoded to integers):\n");
    for (size_t i = 0; i < coe->vector_size; i++) {
        printf("%d", coe->vector[i]);
        if (i < coe->vector_size - 1) printf(", ");
    }
    printf("\n");
}

int coe_write(const char* filename, int radix, const int* vector, size_t vector_size) {
    FILE* fp = fopen(filename, "w");
    if (!fp) return -1;
    
    fprintf(fp, "; Generated .COE file\n");
    fprintf(fp, "memory_initialization_radix = %d;\n", radix);
    fprintf(fp, "memory_initialization_vector =\n");
    for (size_t i = 0; i < vector_size; i++) {
        if (radix == 2) {
            fprintf(fp, "%b", vector[i]);  // Note: %b not standard, use custom conversion if needed
        } else if (radix == 8) {
            fprintf(fp, "%o", vector[i]);
        } else if (radix == 10) {
            fprintf(fp, "%d", vector[i]);
        } else if (radix == 16) {
            char buf[32];
            sprintf(buf, "%x", vector[i]);
            for (char* p = buf; *p; p++) *p = tolower(*p);
            fprintf(fp, "%s", buf);
        } else {
            fclose(fp);
            return -2;  // Unsupported radix
        }
        if (i < vector_size - 1) fprintf(fp, ",\n");
    }
    fprintf(fp, ";\n");
    fclose(fp);
    return 0;
}

// Example usage:
// int main() {
//     COEFile* coe = coe_create();
//     coe_load(coe, "input.coe");
//     coe_print_properties(coe);
//     int vector[] = {0, 1, 2, 3};
//     coe_write("output.coe", 16, vector, 4);
//     coe_destroy(coe);
//     return 0;
// }

Note: For binary output in C, %b is not standard; in practice, implement a custom binary conversion function if required for radix 2.