This repository has been archived by the owner on Oct 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
i2c_blackbox.cc
133 lines (114 loc) · 3.92 KB
/
i2c_blackbox.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "build/sh1107.h"
#include <iostream>
/**
* This code is officially Not Poggers(tm).
*
* We emulate the external interface of i2c.py's I2C module for the benefit of
* the rest of the design, rather than the benefit of the simulation. The
* simulation snoops _our inputs_ and uses those directly.
*/
namespace cxxrtl_design {
struct bb_p_i2c_impl : public bb_p_i2c {
// NOTE(Ch): Wow! This is a very magic number! This specifies how many
// posedges without FIFO activity to wait until we consider the transaction to
// be done and bring "busy" low. Whether this is sufficient will vary
// depending on the users of the real I2C module, and how much leeway it gives
// its users. We might want to consider a rewrite where transaction ends are
// signalled explicitly from the user, but that gets Fucky Wucky if they don't
// actually give input data in time for the I2C bus.
//
// 5 is sufficient for a tight loop, but when e.g. ROMWriter/Scroller do a
// repeated start, they spend a few cycles while reading the length of the
// next segment, and when it's non-zero, we need to wait a little more.
const uint16_t TICKS_TO_WAIT = 7u;
enum {
STATE_IDLE,
STATE_BUSY,
} state;
uint16_t ticks_until_done;
enum {
IN_FIFO_STATE_EMPTY,
IN_FIFO_STATE_FULL,
} in_fifo_state;
uint16_t in_fifo_value;
enum {
OUT_FIFO_STATE_EMPTY,
OUT_FIFO_STATE_FULL,
} out_fifo_state;
uint8_t out_fifo_value;
void reset() override {
this->state = STATE_IDLE;
this->in_fifo_state = IN_FIFO_STATE_EMPTY;
this->in_fifo_value = 0u;
this->out_fifo_state = OUT_FIFO_STATE_EMPTY;
this->out_fifo_value = 0u;
p_busy = wire<1>{0u};
p_ack = wire<1>{1u};
p_in__fifo__w__rdy = wire<1>{1u};
p_out__fifo__r__rdy = wire<1>{0u};
p_out__fifo__r__data = wire<8>{0u};
}
bool eval(performer *performer) override {
bool converged = true;
bool posedge_p_clk = this->posedge_p_clk();
if (posedge_p_clk) {
p_ack.next = p_bb__in__ack;
if (p_out__fifo__r__en && out_fifo_state == OUT_FIFO_STATE_FULL) {
out_fifo_state = OUT_FIFO_STATE_EMPTY;
p_out__fifo__r__rdy.next = value<1>{0u};
}
if (p_bb__in__out__fifo__stb) {
out_fifo_state = OUT_FIFO_STATE_FULL;
out_fifo_value = p_bb__in__out__fifo__data.get<uint8_t>();
p_out__fifo__r__rdy.next = value<1>{1u};
p_out__fifo__r__data.next = value<8>{out_fifo_value};
}
switch (this->state) {
case STATE_IDLE: {
if (p_stb) {
p_busy.next = value<1>{1u};
this->state = STATE_BUSY;
this->ticks_until_done = TICKS_TO_WAIT;
}
break;
}
case STATE_BUSY: {
if (this->in_fifo_state == IN_FIFO_STATE_FULL) {
this->in_fifo_state = IN_FIFO_STATE_EMPTY;
this->ticks_until_done = TICKS_TO_WAIT;
p_in__fifo__w__rdy.next = value<1>{1u};
}
if (--this->ticks_until_done == 0u) {
p_busy.next = value<1>{0u};
this->state = STATE_IDLE;
}
break;
}
}
switch (this->in_fifo_state) {
case IN_FIFO_STATE_EMPTY: {
if (p_in__fifo__w__en) {
this->in_fifo_value = p_in__fifo__w__data.get<uint16_t>();
this->in_fifo_state = IN_FIFO_STATE_FULL;
p_in__fifo__w__rdy.next = value<1>{0u};
}
break;
}
case IN_FIFO_STATE_FULL: {
if (p_in__fifo__w__en) {
std::cerr << "bb_p_i2c_impl: dropping a write: " << std::hex << "0x"
<< p_in__fifo__w__data.get<uint16_t>() << std::endl;
}
break;
}
}
}
return converged;
}
};
std::unique_ptr<bb_p_i2c> bb_p_i2c::create(std::string name,
metadata_map parameters,
metadata_map attributes) {
return std::make_unique<bb_p_i2c_impl>();
}
} // namespace cxxrtl_design