324 lines
7.5 KiB
C
324 lines
7.5 KiB
C
#include "fpga_cell.h"
|
|
#include "fpga_global.h"
|
|
#include "fpga_cell_lut34.h"
|
|
#include "fpga_cell_none.h"
|
|
#include "fpga_cell_wire.h"
|
|
#include <stdlib.h>
|
|
|
|
static struct fpga_cell_type fpga_cell_list[] = {
|
|
{
|
|
.identifier = ' ',
|
|
.cell_new = fpga_cell_none_new,
|
|
.get_next_isp = fpga_cell_none_get_next_isp,
|
|
.isp_length = 0,
|
|
.isp_generate_bitstream = fpga_cell_isp_generate_bitstream_dummy
|
|
},
|
|
{
|
|
.identifier = '[',
|
|
.cell_new = fpga_cell_wire_new,
|
|
.get_next_isp = fpga_cell_wire_get_next_isp,
|
|
.isp_length = 0,
|
|
.isp_generate_bitstream = fpga_cell_isp_generate_bitstream_dummy
|
|
},
|
|
{
|
|
.identifier = ']',
|
|
.cell_new = fpga_cell_wire_new,
|
|
.get_next_isp = fpga_cell_wire_get_next_isp,
|
|
.isp_length = 0,
|
|
.isp_generate_bitstream = fpga_cell_isp_generate_bitstream_dummy
|
|
},
|
|
{
|
|
.identifier = 'P',
|
|
.cell_new = fpga_cell_wire_new,
|
|
.get_next_isp = fpga_cell_wire_get_next_isp,
|
|
.isp_length = 0,
|
|
.isp_generate_bitstream = fpga_cell_isp_generate_bitstream_dummy
|
|
},
|
|
{
|
|
.identifier = 'L',
|
|
.cell_new = fpga_cell_lut34_new,
|
|
.get_next_isp = fpga_cell_lut34_get_next_isp,
|
|
.isp_length = 6,
|
|
.isp_generate_bitstream = fpga_cell_lut34_isp_generate_bitstream
|
|
},
|
|
{
|
|
.identifier = '\0',
|
|
.cell_new = NULL,
|
|
.get_next_isp = NULL,
|
|
.isp_length = 0,
|
|
.isp_generate_bitstream = fpga_cell_isp_generate_bitstream_dummy
|
|
}
|
|
};
|
|
|
|
void fpga_cell_init(struct fpga_cell *cell,
|
|
struct fpga_cell_type *cell_type)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < FPGA_CELL_CONNECTION_COUNT; ++i) {
|
|
cell->cell_connections[i] = NULL;
|
|
}
|
|
|
|
cell->type = cell_type;
|
|
//fpga_cell->get_next_isp = fpga_cell_get_next_isp_dummy;
|
|
|
|
report(LL_DEBUG,
|
|
"Initialized cell of type %d at %p.",
|
|
cell_type, cell);
|
|
}
|
|
|
|
enum fpga_cell_position fpga_cell_connection_opposing(enum fpga_cell_position position)
|
|
{
|
|
switch (position) {
|
|
case LEFT:
|
|
return RIGHT;
|
|
case RIGHT:
|
|
return LEFT;
|
|
case TOP:
|
|
return BOTTOM;
|
|
case BOTTOM:
|
|
return TOP;
|
|
default:
|
|
report(LL_ERROR,
|
|
"Internal error: Requested opposite of invalid position %d.",
|
|
position);
|
|
return LEFT;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief fpga_cell_connect Connect <fpga_cell> to the <position> of <target_cell>
|
|
* @param fpga_cell
|
|
* @param target_cell
|
|
* @param position
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int fpga_cell_connect(struct fpga_cell *fpga_cell,
|
|
struct fpga_cell *target_cell,
|
|
enum fpga_cell_position position)
|
|
{
|
|
if (target_cell->cell_connections[position] ||
|
|
fpga_cell->cell_connections[fpga_cell_connection_opposing(position)]) {
|
|
report(LL_WARNING,
|
|
"Tried to connect cell %p to pos %d of %p, even though a connection already exists for a least one of them.",
|
|
fpga_cell, target_cell, position);
|
|
errno = -ECONN;
|
|
return -1;
|
|
}
|
|
|
|
target_cell->cell_connections[position] = fpga_cell;
|
|
fpga_cell->cell_connections[fpga_cell_connection_opposing(position)] = target_cell;
|
|
|
|
report(LL_DEBUG,
|
|
"Connected cell %p to port %d of %p.",
|
|
fpga_cell, position, target_cell);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief fpga_cell_get_far Get cell on the far <position> of <cell>
|
|
* @param cell
|
|
* @param position
|
|
* @return
|
|
*/
|
|
struct fpga_cell *fpga_cell_get_far(struct fpga_cell *cell,
|
|
enum fpga_cell_position position)
|
|
{
|
|
struct fpga_cell *next = cell;
|
|
|
|
while (next->cell_connections[position]) {
|
|
next = next->cell_connections[position];
|
|
}
|
|
|
|
return next;
|
|
}
|
|
|
|
/**
|
|
* @brief fpga_cell_get_next_isp Get the next cell in the ISP chain
|
|
* @param fpga_cell the current cell in the ISP chain
|
|
* @param isp_channel the current channel. This will be updated when calling this function!
|
|
* @return the next ISP cell
|
|
*/
|
|
struct fpga_cell *fpga_cell_get_next_isp_dummy(struct fpga_cell *cell,
|
|
enum fpga_isp_channel *isp_channel)
|
|
{
|
|
report(LL_CRITICAL,
|
|
"Internal error. Cell type %c does not implement get_next_isp!",
|
|
cell->type->identifier);
|
|
*isp_channel = FPGA_ISP_CHANNEL_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief isp_generate_bitstream_dummy
|
|
* @param cell
|
|
* @param buf
|
|
* @return
|
|
*/
|
|
char *fpga_cell_isp_generate_bitstream_dummy(struct fpga_cell *cell)
|
|
{
|
|
report(LL_CRITICAL,
|
|
"Internal error. Cell type %c does not implement isp_generate_bitstream!",
|
|
cell->type->identifier);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief fpga_cell_new Create a new instance of type <identifier>
|
|
* @param identifier
|
|
* @return
|
|
*/
|
|
struct fpga_cell *fpga_cell_new(char identifier)
|
|
{
|
|
struct fpga_cell_type *cell_type = fpga_cell_list;
|
|
struct fpga_cell *cell = NULL;
|
|
|
|
while (cell_type->identifier) {
|
|
if (identifier == cell_type->identifier) {
|
|
cell = cell_type->cell_new();
|
|
if (!cell) {
|
|
report(LL_CRITICAL,
|
|
"Out of memory when creating cell.");
|
|
return NULL;
|
|
}
|
|
cell->type = cell_type;
|
|
report(LL_DEBUG,
|
|
"Created cell %p of type %c.",
|
|
cell, identifier);
|
|
return cell;
|
|
}
|
|
cell_type++;
|
|
}
|
|
|
|
report(LL_ERROR,
|
|
"Invalid cell type %c found in FDEF.",
|
|
identifier);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief fpga_isp_chain_new
|
|
* @param entry
|
|
* @return
|
|
*/
|
|
struct fpga_chain *fpga_isp_chain_new(struct fpga_cell *entry)
|
|
{
|
|
struct fpga_cell *current_cell = entry;
|
|
enum fpga_isp_channel current_channel = FPGA_ISP_CHANNEL_NORMAL;
|
|
struct fpga_chain *chain;
|
|
|
|
report(LL_INFO,
|
|
"Generating ISP chain...");
|
|
|
|
if (entry->type->identifier != 'P') {
|
|
report(LL_CRITICAL,
|
|
"Entry cell must be of type P, was '%c'",
|
|
entry->type->identifier);
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
chain = calloc(sizeof (struct fpga_chain), 1);
|
|
if (!chain) {
|
|
report (LL_CRITICAL,
|
|
"Out of memory during ISP chain generation.");
|
|
errno = ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
while (current_cell) {
|
|
if (current_channel == FPGA_ISP_CHANNEL_NORMAL &&
|
|
current_cell->type->isp_length > 0) {
|
|
report(LL_DEBUG,
|
|
"Adding %p (%c) to ISP chain.",
|
|
current_cell, current_cell->type->identifier);
|
|
|
|
if (!chain->head) {
|
|
chain->head = malloc(sizeof(chain->head));
|
|
*(chain->head) = current_cell;
|
|
chain->length = 1;
|
|
} else {
|
|
chain->length++;
|
|
chain->head = realloc(chain->head,
|
|
sizeof(chain->head) * chain->length);
|
|
*(chain->head + chain->length - 1) = current_cell;
|
|
}
|
|
|
|
} else {
|
|
report(LL_DEBUG,
|
|
"Using %p (%c) for ISP routing.",
|
|
current_cell, current_cell->type->identifier);
|
|
}
|
|
|
|
current_cell = current_cell->type->get_next_isp(current_cell,
|
|
¤t_channel);
|
|
if (current_channel == FPGA_ISP_CHANNEL_ERROR) {
|
|
/* TODO: Clean up memory */
|
|
errno = ETYPE;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
report(LL_INFO,
|
|
"Added %d cells to ISP chain.",
|
|
chain->length);
|
|
|
|
for (int i = 0; i < chain->length; ++i) {
|
|
report(LL_DEBUG,
|
|
" %4d = %p",
|
|
i, chain->head[i]);
|
|
}
|
|
|
|
report(LL_INFO,
|
|
"ISP chain generation finished.");
|
|
|
|
return chain;
|
|
}
|
|
|
|
int fpga_cell_bitstream_set_bit(char *buffer, char *sbuffer,
|
|
int bit, int value) {
|
|
int byteno;
|
|
int bitno;
|
|
|
|
bitno = bit % 8;
|
|
byteno = bit / 8;
|
|
|
|
if ((sbuffer[byteno] & (1 << bitno))) { /* Already set */
|
|
return value == !((buffer[byteno] & (1 << bitno)));
|
|
} else {
|
|
sbuffer[byteno] |= (1 << bitno);
|
|
if (value)
|
|
buffer[byteno] |= (1 << bitno);
|
|
else
|
|
buffer[byteno] &= ~(1 << bitno);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fpga_cell_bitstream_set_bits(char *buffer, char *sbuffer,
|
|
int value, int nbits, int *mapping)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < nbits; ++i) {
|
|
ret = ret || fpga_cell_bitstream_set_bit(buffer, sbuffer,
|
|
mapping[i], value & (1 << i));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fpga_cell_bitstream_is_set(char *buffer, int bit)
|
|
{
|
|
int byteno;
|
|
int bitno;
|
|
|
|
bitno = bit % 8;
|
|
byteno = bit / 8;
|
|
|
|
return !!(buffer[byteno] & (1 << bitno));
|
|
}
|