mirror of https://github.com/YosysHQ/yosys.git
Added $meminit support to "memory" command
This commit is contained in:
parent
913c304fe6
commit
dcf2e24240
|
@ -917,6 +917,7 @@ namespace {
|
||||||
param("\\MEMID");
|
param("\\MEMID");
|
||||||
param("\\SIZE");
|
param("\\SIZE");
|
||||||
param("\\OFFSET");
|
param("\\OFFSET");
|
||||||
|
param("\\INIT");
|
||||||
param_bits("\\RD_CLK_ENABLE", param("\\RD_PORTS"));
|
param_bits("\\RD_CLK_ENABLE", param("\\RD_PORTS"));
|
||||||
param_bits("\\RD_CLK_POLARITY", param("\\RD_PORTS"));
|
param_bits("\\RD_CLK_POLARITY", param("\\RD_PORTS"));
|
||||||
param_bits("\\RD_TRANSPARENT", param("\\RD_PORTS"));
|
param_bits("\\RD_TRANSPARENT", param("\\RD_PORTS"));
|
||||||
|
|
|
@ -905,6 +905,11 @@ void handle_cell(Cell *cell, const rules_t &rules)
|
||||||
{
|
{
|
||||||
log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
|
log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
|
||||||
|
|
||||||
|
if (!SigSpec(cell->getParam("\\INIT")).is_fully_undef()) {
|
||||||
|
log(" initialized memories are not supported yet.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dict<string, int> match_properties;
|
dict<string, int> match_properties;
|
||||||
match_properties["words"] = cell->getParam("\\SIZE").as_int();
|
match_properties["words"] = cell->getParam("\\SIZE").as_int();
|
||||||
match_properties["abits"] = cell->getParam("\\ABITS").as_int();
|
match_properties["abits"] = cell->getParam("\\ABITS").as_int();
|
||||||
|
|
|
@ -17,11 +17,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "kernel/register.h"
|
#include "kernel/yosys.h"
|
||||||
#include "kernel/log.h"
|
#include "kernel/sigtools.h"
|
||||||
#include <sstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
@ -37,13 +34,16 @@ bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
|
||||||
|
|
||||||
void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||||
{
|
{
|
||||||
log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n",
|
log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n",
|
||||||
memory->name.c_str(), module->name.c_str());
|
memory->name.c_str(), module->name.c_str());
|
||||||
|
|
||||||
int addr_bits = 0;
|
int addr_bits = 0;
|
||||||
while ((1 << addr_bits) < memory->size)
|
while ((1 << addr_bits) < memory->size)
|
||||||
addr_bits++;
|
addr_bits++;
|
||||||
|
|
||||||
|
Const init_data(State::Sx, memory->size * memory->width);
|
||||||
|
SigMap sigmap(module);
|
||||||
|
|
||||||
int wr_ports = 0;
|
int wr_ports = 0;
|
||||||
RTLIL::SigSpec sig_wr_clk;
|
RTLIL::SigSpec sig_wr_clk;
|
||||||
RTLIL::SigSpec sig_wr_clk_enable;
|
RTLIL::SigSpec sig_wr_clk_enable;
|
||||||
|
@ -60,12 +60,11 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||||
RTLIL::SigSpec sig_rd_addr;
|
RTLIL::SigSpec sig_rd_addr;
|
||||||
RTLIL::SigSpec sig_rd_data;
|
RTLIL::SigSpec sig_rd_data;
|
||||||
|
|
||||||
std::vector<RTLIL::Cell*> del_cells;
|
|
||||||
std::vector<RTLIL::Cell*> memcells;
|
std::vector<RTLIL::Cell*> memcells;
|
||||||
|
|
||||||
for (auto &cell_it : module->cells_) {
|
for (auto &cell_it : module->cells_) {
|
||||||
RTLIL::Cell *cell = cell_it.second;
|
RTLIL::Cell *cell = cell_it.second;
|
||||||
if ((cell->type == "$memwr" || cell->type == "$memrd") && memory->name == cell->parameters["\\MEMID"].decode_string())
|
if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string())
|
||||||
memcells.push_back(cell);
|
memcells.push_back(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,17 +72,38 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||||
|
|
||||||
for (auto cell : memcells)
|
for (auto cell : memcells)
|
||||||
{
|
{
|
||||||
if (cell->type == "$memwr" && memory->name == cell->parameters["\\MEMID"].decode_string())
|
log(" %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||||
{
|
|
||||||
wr_ports++;
|
|
||||||
del_cells.push_back(cell);
|
|
||||||
|
|
||||||
RTLIL::SigSpec clk = cell->getPort("\\CLK");
|
if (cell->type == "$meminit")
|
||||||
RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
|
{
|
||||||
RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
|
SigSpec addr = sigmap(cell->getPort("\\ADDR"));
|
||||||
RTLIL::SigSpec addr = cell->getPort("\\ADDR");
|
SigSpec data = sigmap(cell->getPort("\\DATA"));
|
||||||
RTLIL::SigSpec data = cell->getPort("\\DATA");
|
|
||||||
RTLIL::SigSpec en = cell->getPort("\\EN");
|
if (!addr.is_fully_const())
|
||||||
|
log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
|
||||||
|
if (!data.is_fully_const())
|
||||||
|
log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
|
||||||
|
|
||||||
|
int offset = (addr.as_int() - memory->start_offset) * memory->width;
|
||||||
|
|
||||||
|
if (offset < 0 || offset + GetSize(data) > GetSize(init_data))
|
||||||
|
log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell));
|
||||||
|
|
||||||
|
for (int i = 0; i < GetSize(data); i++)
|
||||||
|
if (0 <= i+offset && i+offset < GetSize(init_data))
|
||||||
|
init_data.bits[i+offset] = data[i].data;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell->type == "$memwr")
|
||||||
|
{
|
||||||
|
SigSpec clk = sigmap(cell->getPort("\\CLK"));
|
||||||
|
SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
|
||||||
|
SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
|
||||||
|
SigSpec addr = sigmap(cell->getPort("\\ADDR"));
|
||||||
|
SigSpec data = sigmap(cell->getPort("\\DATA"));
|
||||||
|
SigSpec en = sigmap(cell->getPort("\\EN"));
|
||||||
|
|
||||||
clk.extend_u0(1, false);
|
clk.extend_u0(1, false);
|
||||||
clk_enable.extend_u0(1, false);
|
clk_enable.extend_u0(1, false);
|
||||||
|
@ -98,19 +118,19 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||||
sig_wr_addr.append(addr);
|
sig_wr_addr.append(addr);
|
||||||
sig_wr_data.append(data);
|
sig_wr_data.append(data);
|
||||||
sig_wr_en.append(en);
|
sig_wr_en.append(en);
|
||||||
|
|
||||||
|
wr_ports++;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type == "$memrd" && memory->name == cell->parameters["\\MEMID"].decode_string())
|
if (cell->type == "$memrd")
|
||||||
{
|
{
|
||||||
rd_ports++;
|
SigSpec clk = sigmap(cell->getPort("\\CLK"));
|
||||||
del_cells.push_back(cell);
|
SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
|
||||||
|
SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
|
||||||
RTLIL::SigSpec clk = cell->getPort("\\CLK");
|
SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]);
|
||||||
RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
|
SigSpec addr = sigmap(cell->getPort("\\ADDR"));
|
||||||
RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
|
SigSpec data = sigmap(cell->getPort("\\DATA"));
|
||||||
RTLIL::SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]);
|
|
||||||
RTLIL::SigSpec addr = cell->getPort("\\ADDR");
|
|
||||||
RTLIL::SigSpec data = cell->getPort("\\DATA");
|
|
||||||
|
|
||||||
clk.extend_u0(1, false);
|
clk.extend_u0(1, false);
|
||||||
clk_enable.extend_u0(1, false);
|
clk_enable.extend_u0(1, false);
|
||||||
|
@ -125,6 +145,9 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||||
sig_rd_transparent.append(transparent);
|
sig_rd_transparent.append(transparent);
|
||||||
sig_rd_addr.append(addr);
|
sig_rd_addr.append(addr);
|
||||||
sig_rd_data.append(data);
|
sig_rd_data.append(data);
|
||||||
|
|
||||||
|
rd_ports++;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,6 +161,10 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||||
mem->parameters["\\SIZE"] = RTLIL::Const(memory->size);
|
mem->parameters["\\SIZE"] = RTLIL::Const(memory->size);
|
||||||
mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits);
|
mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits);
|
||||||
|
|
||||||
|
while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx)
|
||||||
|
init_data.bits.pop_back();
|
||||||
|
mem->parameters["\\INIT"] = init_data;
|
||||||
|
|
||||||
log_assert(sig_wr_clk.size() == wr_ports);
|
log_assert(sig_wr_clk.size() == wr_ports);
|
||||||
log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const());
|
log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const());
|
||||||
log_assert(sig_wr_clk_polarity.size() == wr_ports && sig_wr_clk_polarity.is_fully_const());
|
log_assert(sig_wr_clk_polarity.size() == wr_ports && sig_wr_clk_polarity.is_fully_const());
|
||||||
|
@ -169,7 +196,7 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||||
mem->setPort("\\RD_ADDR", sig_rd_addr);
|
mem->setPort("\\RD_ADDR", sig_rd_addr);
|
||||||
mem->setPort("\\RD_DATA", sig_rd_data);
|
mem->setPort("\\RD_DATA", sig_rd_data);
|
||||||
|
|
||||||
for (auto c : del_cells)
|
for (auto c : memcells)
|
||||||
module->remove(c);
|
module->remove(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,11 +80,15 @@ struct MemoryMapWorker
|
||||||
{
|
{
|
||||||
std::set<int> static_ports;
|
std::set<int> static_ports;
|
||||||
std::map<int, RTLIL::SigSpec> static_cells_map;
|
std::map<int, RTLIL::SigSpec> static_cells_map;
|
||||||
|
|
||||||
int mem_size = cell->parameters["\\SIZE"].as_int();
|
int mem_size = cell->parameters["\\SIZE"].as_int();
|
||||||
int mem_width = cell->parameters["\\WIDTH"].as_int();
|
int mem_width = cell->parameters["\\WIDTH"].as_int();
|
||||||
int mem_offset = cell->parameters["\\OFFSET"].as_int();
|
// int mem_offset = cell->parameters["\\OFFSET"].as_int();
|
||||||
int mem_abits = cell->parameters["\\ABITS"].as_int();
|
int mem_abits = cell->parameters["\\ABITS"].as_int();
|
||||||
|
|
||||||
|
SigSpec init_data = cell->getParam("\\INIT");
|
||||||
|
init_data.extend_u0(mem_size*mem_width, true);
|
||||||
|
|
||||||
// delete unused memory cell
|
// delete unused memory cell
|
||||||
if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) {
|
if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) {
|
||||||
module->remove(cell);
|
module->remove(cell);
|
||||||
|
@ -165,7 +169,10 @@ struct MemoryMapWorker
|
||||||
w_out_name = genid(cell->name, "", i, "$q");
|
w_out_name = genid(cell->name, "", i, "$q");
|
||||||
|
|
||||||
RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width);
|
RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width);
|
||||||
w_out->start_offset = mem_offset;
|
SigSpec w_init = init_data.extract(i*mem_width, mem_width);
|
||||||
|
|
||||||
|
if (!w_init.is_fully_undef())
|
||||||
|
w_out->attributes["\\init"] = w_init.as_const();
|
||||||
|
|
||||||
data_reg_out.push_back(RTLIL::SigSpec(w_out));
|
data_reg_out.push_back(RTLIL::SigSpec(w_out));
|
||||||
c->setPort("\\Q", data_reg_out.back());
|
c->setPort("\\Q", data_reg_out.back());
|
||||||
|
|
|
@ -1543,6 +1543,7 @@ parameter SIZE = 256;
|
||||||
parameter OFFSET = 0;
|
parameter OFFSET = 0;
|
||||||
parameter ABITS = 8;
|
parameter ABITS = 8;
|
||||||
parameter WIDTH = 8;
|
parameter WIDTH = 8;
|
||||||
|
parameter signed INIT = 1'bx;
|
||||||
|
|
||||||
parameter RD_PORTS = 1;
|
parameter RD_PORTS = 1;
|
||||||
parameter RD_CLK_ENABLE = 1'b1;
|
parameter RD_CLK_ENABLE = 1'b1;
|
||||||
|
@ -1583,25 +1584,36 @@ function port_active;
|
||||||
end
|
end
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
for (i = 0; i < SIZE; i = i+1)
|
||||||
|
memory[i] = INIT >>> (i*WIDTH);
|
||||||
|
end
|
||||||
|
|
||||||
always @(RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA) begin
|
always @(RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA) begin
|
||||||
`ifdef SIMLIB_MEMDELAY
|
`ifdef SIMLIB_MEMDELAY
|
||||||
#`SIMLIB_MEMDELAY;
|
#`SIMLIB_MEMDELAY;
|
||||||
`endif
|
`endif
|
||||||
for (i = 0; i < RD_PORTS; i = i+1) begin
|
for (i = 0; i < RD_PORTS; i = i+1) begin
|
||||||
if ((!RD_TRANSPARENT[i] && RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i]))
|
if ((!RD_TRANSPARENT[i] && RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin
|
||||||
|
// $display("Read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]);
|
||||||
RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
|
RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for (i = 0; i < WR_PORTS; i = i+1) begin
|
for (i = 0; i < WR_PORTS; i = i+1) begin
|
||||||
if (port_active(WR_CLK_ENABLE[i], WR_CLK_POLARITY[i], LAST_WR_CLK[i], WR_CLK[i]))
|
if (port_active(WR_CLK_ENABLE[i], WR_CLK_POLARITY[i], LAST_WR_CLK[i], WR_CLK[i]))
|
||||||
for (j = 0; j < WIDTH; j = j+1)
|
for (j = 0; j < WIDTH; j = j+1)
|
||||||
if (WR_EN[i*WIDTH+j])
|
if (WR_EN[i*WIDTH+j]) begin
|
||||||
|
// $display("Write to %s: addr=%b data=%b", MEMID, WR_ADDR[i*ABITS +: ABITS], WR_DATA[i*WIDTH+j]);
|
||||||
memory[WR_ADDR[i*ABITS +: ABITS] - OFFSET][j] = WR_DATA[i*WIDTH+j];
|
memory[WR_ADDR[i*ABITS +: ABITS] - OFFSET][j] = WR_DATA[i*WIDTH+j];
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for (i = 0; i < RD_PORTS; i = i+1) begin
|
for (i = 0; i < RD_PORTS; i = i+1) begin
|
||||||
if ((RD_TRANSPARENT[i] || !RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i]))
|
if ((RD_TRANSPARENT[i] || !RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin
|
||||||
|
// $display("Transparent read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]);
|
||||||
RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
|
RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
LAST_RD_CLK <= RD_CLK;
|
LAST_RD_CLK <= RD_CLK;
|
||||||
|
|
|
@ -209,29 +209,22 @@ endmodule
|
||||||
|
|
||||||
module memtest09 (
|
module memtest09 (
|
||||||
input clk,
|
input clk,
|
||||||
input [1:0] a_addr, a_din, b_addr, b_din,
|
input [3:0] a_addr, a_din, b_addr, b_din,
|
||||||
input a_wen, b_wen,
|
input a_wen, b_wen,
|
||||||
output reg [1:0] a_dout, b_dout
|
output reg [3:0] a_dout, b_dout
|
||||||
);
|
);
|
||||||
reg [1:0] memory [0:3];
|
reg [3:0] memory [0:35];
|
||||||
|
|
||||||
initial begin
|
|
||||||
memory[0] <= 0;
|
|
||||||
memory[1] <= 1;
|
|
||||||
memory[2] <= 2;
|
|
||||||
memory[3] <= 3;
|
|
||||||
end
|
|
||||||
|
|
||||||
always @(posedge clk) begin
|
always @(posedge clk) begin
|
||||||
if (a_wen)
|
if (a_wen)
|
||||||
memory[a_addr] <= a_din;
|
memory[10 + a_addr] <= a_din;
|
||||||
a_dout <= memory[a_addr];
|
a_dout <= memory[10 + a_addr];
|
||||||
end
|
end
|
||||||
|
|
||||||
always @(posedge clk) begin
|
always @(posedge clk) begin
|
||||||
if (b_wen)
|
if (b_wen && (10 + a_addr != 20 + b_addr))
|
||||||
memory[b_addr] <= b_din;
|
memory[20 + b_addr] <= b_din;
|
||||||
b_dout <= memory[b_addr];
|
b_dout <= memory[20 + b_addr];
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA);
|
||||||
parameter OFFSET = 0;
|
parameter OFFSET = 0;
|
||||||
parameter ABITS = 8;
|
parameter ABITS = 8;
|
||||||
parameter WIDTH = 8;
|
parameter WIDTH = 8;
|
||||||
|
parameter signed INIT = 1'bx;
|
||||||
|
|
||||||
parameter RD_PORTS = 1;
|
parameter RD_PORTS = 1;
|
||||||
parameter RD_CLK_ENABLE = 1'b1;
|
parameter RD_CLK_ENABLE = 1'b1;
|
||||||
|
@ -37,6 +38,10 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA);
|
||||||
initial begin
|
initial begin
|
||||||
_TECHMAP_FAIL_ <= 0;
|
_TECHMAP_FAIL_ <= 0;
|
||||||
|
|
||||||
|
// no initialized memories
|
||||||
|
if (INIT !== 1'bx)
|
||||||
|
_TECHMAP_FAIL_ <= 1;
|
||||||
|
|
||||||
// only map cells with only one read and one write port
|
// only map cells with only one read and one write port
|
||||||
if (RD_PORTS > 1 || WR_PORTS > 1)
|
if (RD_PORTS > 1 || WR_PORTS > 1)
|
||||||
_TECHMAP_FAIL_ <= 1;
|
_TECHMAP_FAIL_ <= 1;
|
||||||
|
|
Loading…
Reference in New Issue