/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #ifndef MODTOOLS_H #define MODTOOLS_H #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" YOSYS_NAMESPACE_BEGIN struct ModIndex : public RTLIL::Monitor { struct PortInfo { RTLIL::Cell* cell; RTLIL::IdString port; int offset; PortInfo() : cell(), port(), offset() { } PortInfo(RTLIL::Cell* _c, RTLIL::IdString _p, int _o) : cell(_c), port(_p), offset(_o) { } bool operator<(const PortInfo &other) const { if (cell != other.cell) return cell < other.cell; if (offset != other.offset) return offset < other.offset; return port < other.port; } bool operator==(const PortInfo &other) const { return cell == other.cell && port == other.port && offset == other.offset; } unsigned int hash() const { return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset); } }; struct SigBitInfo { bool is_input, is_output; pool ports; SigBitInfo() : is_input(false), is_output(false) { } bool operator==(const SigBitInfo &other) const { return is_input == other.is_input && is_output == other.is_output && ports == other.ports; } void merge(const SigBitInfo &other) { is_input = is_input || other.is_input; is_output = is_output || other.is_output; ports.insert(other.ports.begin(), other.ports.end()); } }; SigMap sigmap; RTLIL::Module *module; std::map database; int auto_reload_counter; bool auto_reload_module; void port_add(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &sig) { for (int i = 0; i < GetSize(sig); i++) { RTLIL::SigBit bit = sigmap(sig[i]); if (bit.wire) database[bit].ports.insert(PortInfo(cell, port, i)); } } void port_del(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &sig) { for (int i = 0; i < GetSize(sig); i++) { RTLIL::SigBit bit = sigmap(sig[i]); if (bit.wire) database[bit].ports.erase(PortInfo(cell, port, i)); } } const SigBitInfo &info(RTLIL::SigBit bit) { return database[sigmap(bit)]; } void reload_module(bool reset_sigmap = true) { if (reset_sigmap) { sigmap.clear(); sigmap.set(module); } database.clear(); for (auto wire : module->wires()) if (wire->port_input || wire->port_output) for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigBit bit = sigmap(RTLIL::SigBit(wire, i)); if (bit.wire && wire->port_input) database[bit].is_input = true; if (bit.wire && wire->port_output) database[bit].is_output = true; } for (auto cell : module->cells()) for (auto &conn : cell->connections()) port_add(cell, conn.first, conn.second); if (auto_reload_module) { if (++auto_reload_counter > 2) log_warning("Auto-reload in ModIndex -- possible performance bug!\n"); auto_reload_module = false; } } void check() { #ifndef NDEBUG if (auto_reload_module) return; for (auto it : database) log_assert(it.first == sigmap(it.first)); auto database_bak = std::move(database); reload_module(false); if (!(database == database_bak)) { for (auto &it : database_bak) if (!database.count(it.first)) log("ModuleIndex::check(): Only in database_bak, not database: %s\n", log_signal(it.first)); for (auto &it : database) if (!database_bak.count(it.first)) log("ModuleIndex::check(): Only in database, not database_bak: %s\n", log_signal(it.first)); else if (!(it.second == database_bak.at(it.first))) log("ModuleIndex::check(): Different content for database[%s].\n", log_signal(it.first)); log_assert(database == database_bak); } #endif } void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE { log_assert(module == cell->module); if (auto_reload_module) return; port_del(cell, port, old_sig); port_add(cell, port, sig); } void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const RTLIL::SigSig &sigsig) YS_OVERRIDE { log_assert(module == mod); if (auto_reload_module) return; for (int i = 0; i < GetSize(sigsig.first); i++) { RTLIL::SigBit lhs = sigmap(sigsig.first[i]); RTLIL::SigBit rhs = sigmap(sigsig.second[i]); bool has_lhs = database.count(lhs) != 0; bool has_rhs = database.count(rhs) != 0; if (!has_lhs && !has_rhs) { sigmap.add(lhs, rhs); } else if (!has_rhs) { SigBitInfo new_info = database.at(lhs); database.erase(lhs); sigmap.add(lhs, rhs); lhs = sigmap(lhs); if (lhs.wire) database[lhs] = new_info; } else if (!has_lhs) { SigBitInfo new_info = database.at(rhs); database.erase(rhs); sigmap.add(lhs, rhs); rhs = sigmap(rhs); if (rhs.wire) database[rhs] = new_info; } else { SigBitInfo new_info = database.at(lhs); new_info.merge(database.at(rhs)); database.erase(lhs); database.erase(rhs); sigmap.add(lhs, rhs); rhs = sigmap(rhs); if (rhs.wire) database[rhs] = new_info; } } } void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const std::vector&) YS_OVERRIDE { log_assert(module == mod); auto_reload_module = true; } void notify_blackout(RTLIL::Module *mod YS_ATTRIBUTE(unused)) YS_OVERRIDE { log_assert(module == mod); auto_reload_module = true; } ModIndex(RTLIL::Module *_m) : sigmap(_m), module(_m) { auto_reload_counter = 0; auto_reload_module = true; module->monitors.insert(this); } ~ModIndex() { module->monitors.erase(this); } SigBitInfo *query(RTLIL::SigBit bit) { if (auto_reload_module) reload_module(); auto it = database.find(sigmap(bit)); if (it == database.end()) return nullptr; else return &it->second; } bool query_is_input(RTLIL::SigBit bit) { const SigBitInfo *info = query(bit); if (info == nullptr) return false; return info->is_input; } bool query_is_output(RTLIL::SigBit bit) { const SigBitInfo *info = query(bit); if (info == nullptr) return false; return info->is_output; } pool &query_ports(RTLIL::SigBit bit) { static pool empty_result_set; SigBitInfo *info = query(bit); if (info == nullptr) return empty_result_set; return info->ports; } void dump_db() { log("--- ModIndex Dump ---\n"); if (auto_reload_module) { log("AUTO-RELOAD\n"); reload_module(); } for (auto &it : database) { log("BIT %s:\n", log_signal(it.first)); if (it.second.is_input) log(" PRIMARY INPUT\n"); if (it.second.is_output) log(" PRIMARY OUTPUT\n"); for (auto &port : it.second.ports) log(" PORT: %s.%s[%d] (%s)\n", log_id(port.cell), log_id(port.port), port.offset, log_id(port.cell->type)); } } }; struct ModWalker { struct PortBit { RTLIL::Cell *cell; RTLIL::IdString port; int offset; bool operator<(const PortBit &other) const { if (cell != other.cell) return cell < other.cell; if (port != other.port) return port < other.port; return offset < other.offset; } bool operator==(const PortBit &other) const { return cell == other.cell && port == other.port && offset == other.offset; } unsigned int hash() const { return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset); } }; RTLIL::Design *design; RTLIL::Module *module; CellTypes ct; SigMap sigmap; dict> signal_drivers; dict> signal_consumers; pool signal_inputs, signal_outputs; dict> cell_outputs, cell_inputs; void add_wire(RTLIL::Wire *wire) { if (wire->port_input) { std::vector bits = sigmap(wire); for (auto bit : bits) if (bit.wire != NULL) signal_inputs.insert(bit); } if (wire->port_output) { std::vector bits = sigmap(wire); for (auto bit : bits) if (bit.wire != NULL) signal_outputs.insert(bit); } } void add_cell_port(RTLIL::Cell *cell, RTLIL::IdString port, std::vector bits, bool is_output, bool is_input) { for (int i = 0; i < int(bits.size()); i++) if (bits[i].wire != NULL) { PortBit pbit = { cell, port, i }; if (is_output) { signal_drivers[bits[i]].insert(pbit); cell_outputs[cell].insert(bits[i]);
/*
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
*/
#include <stdbool.h>
#include <util/delay.h>
#include "debug.h"
#include "ring_buffer.h"
#include "ibm4704.h"

#define WAIT(stat, us, err)      \
    do {                         \
        if (!wait_##stat(us)) {  \
            ibm4704_error = err; \
            goto ERROR;          \
        }                        \
    } while (0)

uint8_t ibm4704_error = 0;

void ibm4704_init(void) {
    inhibit();  // keep keyboard from sending
    IBM4704_INT_INIT();
    IBM4704_INT_ON();
    idle();  // allow keyboard sending
}

/*
Host to Keyboard
----------------
Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.

        ____        __   __   __   __   __   __   __   __   __   ________
Clock       \______/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/
            ^   ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ___
Data    ____|__/    X____X____X____X____X____X____X____X____X____X   \___
            |  Start   0    1    2    3    4    5    6    7    P   Stop
            Request by host

Start bit:  can be long as 300-350us.
Request:    Host pulls Clock line down to request to send a command.
Timing:     After Request keyboard pull up Data and down Clock line to low for start bit.
            After request host release Clock line once Data line becomes hi.
            Host writes a bit while Clock is hi and Keyboard reads while low.
Stop bit:   Host releases or pulls up Data line to hi after 9th clock and waits for keyboard pull down the line to lo.
*/
uint8_t ibm4704_send(uint8_t data) {
    bool parity   = true;  // odd parity
    ibm4704_error = 0;

    IBM4704_INT_OFF();

    /* Request to send */
    idle();
    clock_lo();

    /* wait for Start bit(Clock:lo/Data:hi) */
    WAIT(data_hi, 300, 0x30);

    /* Data bit */
    for (uint8_t i = 0; i < 8; i++) {
        WAIT(clock_hi, 100, 0x40 + i);
        if (data & (1 << i)) {
            parity = !parity;
            data_hi();
        } else {
            data_lo();
        }
        WAIT(clock_lo, 100, 0x48 + i);
    }

    /* Parity bit */
    WAIT(clock_hi, 100, 0x34);
    if (parity) {
        data_hi();
    } else {
        data_lo();
    }
    WAIT(clock_lo, 100, 0x35);

    /* Stop bit */
    WAIT(clock_hi, 100, 0x34);
    data_hi();

    /* End */
    WAIT(data_lo, 100, 0x36);

    idle();
    IBM4704_INT_ON();
    return 0;
ERROR:
    idle();
    if (ibm4704_error > 0x30) {
        xprintf("S:%02X ", ibm4704_error);
    }
    IBM4704_INT_ON();
    return -1;
}

/* wait forever to receive data */
uint8_t ibm4704_recv_response(void) {
    while (!rbuf_has_data()) {
        _delay_ms(1);
    }
    return rbuf_dequeue();
}

uint8_t ibm4704_recv(void) {
    if (rbuf_has_data()) {
        return rbuf_dequeue();
    } else {
        return -1;
    }
}

/*
Keyboard to Host
----------------
Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.

        ____       __   __   __   __   __   __   __   __   __   _______
Clock       \_____/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/
             ____ ____ ____ ____ ____ ____ ____ ____ ____ ____
Data    ____/    X____X____X____X____X____X____X____X____X____X________
            Start   0    1    2    3    4    5    6    7    P  Stop

Start bit:  can be long as 300-350us.
Inhibit:    Pull Data line down to inhibit keyboard to send.
Timing:     Host reads bit while Clock is hi.(rising edge)
Stop bit:   Keyboard pulls down Data line to lo after 9th clock.
*/
ISR(IBM4704_INT_VECT) {
    static enum { BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, STOP } state = BIT0;
    // LSB first
    static uint8_t data = 0;
    // Odd parity
    static uint8_t parity = false;

    ibm4704_error = 0;

    switch (state) {
        case BIT0:
        case BIT1:
        case BIT2:
        case BIT3:
        case BIT4:
        case BIT5:
        case BIT6:
        case BIT7:
            data >>= 1;
            if (data_in()) {
                data |= 0x80;
                parity = !parity;
            }
            break;
        case PARITY:
            if (data_in()) {
                parity = !parity;
            }
            if (!parity) goto ERROR;
            break;
        case STOP:
            // Data:Low
            WAIT(data_lo, 100, state);
            rbuf_enqueue(data);
            ibm4704_error = IBM4704_ERR_NONE;
            goto DONE;
            break;
        default:
            goto ERROR;
    }
    state++;
    goto RETURN;
ERROR:
    ibm4704_error = state;
    while (ibm4704_send(0xFE)) _delay_ms(1);  // resend
    xprintf("R:%02X%02X\n", state, data);
DONE:
    state  = BIT0;
    data   = 0;
    parity = false;
RETURN:
    return;
}