Skip to content

Commit 69ff14b

Browse files
committed
design reload implemented
1 parent 329f667 commit 69ff14b

File tree

10 files changed

+160
-43
lines changed

10 files changed

+160
-43
lines changed

src/design_tree_panel.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,23 @@
1111
namespace sv {
1212

1313
DesignTreePanel::DesignTreePanel() {
14-
// Populate an intial list of instances, top modules really.
14+
Initialize();
15+
}
16+
17+
void DesignTreePanel::HandleReloadedDesign() {
18+
roots_.clear();
19+
data_.Clear();
20+
line_idx_ = 0;
21+
// TODO: This resets the tree, which causes the full tree to collapse when a design-reload
22+
// happens. Instead, the tree should be expanded to match the prior expansion a much as possible
23+
// with the new design hierarchy after reload. Right now this is accomplished when source is
24+
// loaded via the source-to-design-tree mechanism, but that only works if the source panel is
25+
// focused.
26+
Initialize();
27+
}
28+
29+
void DesignTreePanel::Initialize() {
30+
// Populate an initial list of instances, top modules really.
1531
for (const slang::ast::InstanceSymbol *top : Workspace::Get().Design()->topInstances) {
1632
roots_.push_back(std::make_unique<DesignTreeItem>(top));
1733
}

src/design_tree_panel.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ namespace sv {
1111
class DesignTreePanel : public TreePanel {
1212
public:
1313
DesignTreePanel();
14+
void Initialize();
1415
void UIChar(int ch) final;
1516
std::vector<Tooltip> Tooltips() const final;
1617
// Get the item that should be shown in the source panel, if any.
1718
std::optional<const slang::ast::Symbol *> ItemForSource();
1819
// Opens the tree to the selected item.
1920
void SetItem(const slang::ast::Symbol *item);
2021
bool Searchable() const final { return true; }
22+
void HandleReloadedDesign() final;
2123

2224
private:
2325
std::vector<std::unique_ptr<DesignTreeItem>> roots_;

src/main.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ int main(int argc, char *argv[]) {
1010
// Start the UI and the event loop.
1111
sv::UI ui;
1212
ui.EventLoop();
13+
if (!ui.FinalMessage().empty()) {
14+
std::cout << ui.FinalMessage() << "\n";
15+
}
1316

1417
return 0;
1518
}

src/panel.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class Panel : public TextReceiver {
4848
std::string Error() const;
4949
virtual void PrepareForWaveDataReload() {}
5050
virtual void HandleReloadedWaves() {}
51+
virtual void PrepareForDesignReload() {}
52+
virtual void HandleReloadedDesign() {}
5153

5254
protected:
5355
virtual int NumLines() const = 0;

src/source_panel.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "color.h"
55
#include "radix.h"
66
#include "slang/ast/ASTVisitor.h"
7+
#include "slang/ast/Compilation.h"
78
#include "slang/ast/Expression.h"
89
#include "slang/ast/Symbol.h"
910
#include "slang/ast/expressions/CallExpression.h"
@@ -897,4 +898,56 @@ std::vector<Tooltip> SourcePanel::Tooltips() const {
897898
return tt;
898899
}
899900

901+
void SourcePanel::PrepareForDesignReload() {
902+
stack_idx_ = 0;
903+
state_stack_.clear();
904+
reload_path_.clear();
905+
drivers_or_loads_.clear();
906+
src_info_.clear();
907+
reload_line_idx_ = line_idx_;
908+
if (scope_ != nullptr) reload_path_ = scope_->asSymbol().getHierarchicalPath();
909+
scope_ = nullptr;
910+
}
911+
912+
void SourcePanel::HandleReloadedDesign() {
913+
if (reload_path_.empty()) return;
914+
size_t limit = reload_path_.size();
915+
// This is kinda heavy-handed. It would be better to search level by level, but this works well
916+
// enough. Going level by level is complicated with instance indicies, it would require parsing
917+
// things like "top.block_a.block_b[4].block_c[2].block_d", the square brackets. But identifiers
918+
// could be escaped and contain square brackets that are not real index operations.
919+
while (true) {
920+
std::string_view path(reload_path_.data(), limit);
921+
auto visitor = slang::ast::makeVisitor(
922+
[&](auto &visitor, const slang::ast::InstanceSymbol &inst) {
923+
if (inst.getHierarchicalPath() == path) {
924+
scope_ = &inst.body;
925+
} else {
926+
visitor.visitDefault(inst);
927+
}
928+
},
929+
[&](auto &visitor, const slang::ast::GenerateBlockSymbol &gen) {
930+
if (gen.isUninstantiated) return;
931+
if (gen.getHierarchicalPath() == path) {
932+
scope_ = &gen;
933+
} else {
934+
visitor.visitDefault(gen);
935+
}
936+
});
937+
Workspace::Get().Design()->visit(visitor);
938+
if (scope_ != nullptr) break;
939+
// No design was found, trim the path back to the next hierarchy level and try again.
940+
limit = path.find_last_of('.');
941+
// If there is no more trimming to be done then give up and leave a null scope.
942+
if (limit == std::string_view::npos) break;
943+
}
944+
945+
if (scope_ == nullptr) return;
946+
947+
SetItem(&scope_->asSymbol());
948+
SetLineAndScroll(reload_line_idx_);
949+
// This causes the design tree to be properly re-traversed on reload.
950+
item_for_design_tree_ = &scope_->asSymbol();
951+
}
952+
900953
} // namespace sv

src/source_panel.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class SourcePanel : public Panel {
3030
// Need to look for stuff under the cursor when changing lines.
3131
void SetLineAndScroll(int l) final;
3232
void Resized() final;
33+
void PrepareForDesignReload() final;
34+
void HandleReloadedDesign() final;
3335

3436
private:
3537
void SaveState();
@@ -99,6 +101,10 @@ class SourcePanel : public Panel {
99101
};
100102
std::deque<State> state_stack_;
101103
int stack_idx_ = 0;
104+
// When reloading the design, save the current scope as a textual path, so it can be re-found
105+
// after the reload has finished.
106+
std::string reload_path_;
107+
int reload_line_idx_ = 0;
102108
};
103109

104110
} // namespace sv

src/ui.cc

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ void UI::UpdateTooltips() {
6363
tooltips_.clear();
6464
// Add the tooltips that are always present.
6565
tooltips_.push_back({"C-q", "quit"});
66+
tooltips_.push_back({.hotkeys = "C-r", .description = "Reload"});
6667
if (layout_.has_waves && (panels_[focused_panel_idx_] == waves_panel_.get() ||
6768
panels_[focused_panel_idx_] == wave_tree_panel_.get() ||
6869
panels_[focused_panel_idx_] == wave_signals_panel_.get())) {
6970
tooltips_.push_back(
7071
{.hotkeys = "C-p",
7172
.description =
7273
std::string(layout_.show_wave_picker ? "SHOW/hide" : "show/HIDE") + " picker"});
73-
tooltips_.push_back({.hotkeys = "C-r", .description = "Reload waves"});
7474
}
7575
if (panels_[focused_panel_idx_]->Searchable()) {
7676
tooltips_.push_back({"/nN", "search"});
@@ -148,24 +148,36 @@ UI::UI() : search_box_("/") {
148148
Draw();
149149
}
150150

151-
UI::~UI() {
152-
// Cleanup ncurses
153-
endwin();
154-
}
155-
156-
void UI::ReloadWaves() {
151+
bool UI::Reload() {
157152
for (Panel *p : panels_) {
158-
p->PrepareForWaveDataReload();
153+
if (layout_.has_design) p->PrepareForDesignReload();
154+
if (layout_.has_waves) p->PrepareForWaveDataReload();
155+
}
156+
if (layout_.has_waves) Workspace::Get().Waves()->Reload();
157+
if (layout_.has_design) {
158+
// TODO: This can potentially take a long time. Find some way to show a busy message.
159+
const bool success = Workspace::Get().ReParse();
160+
if (!success) {
161+
error_message_ = "Errors reading design.";
162+
}
163+
// Something went badly wrong if there is no design at all now.
164+
if (Workspace::Get().Design() == nullptr) {
165+
final_message_ = "Fatal error re-parsing design. Check the design for syntax errors.";
166+
return false;
167+
}
159168
}
160-
Workspace::Get().Waves()->Reload();
161-
Workspace::Get().TryMatchDesignWithWaves();
169+
if (layout_.has_design && layout_.has_waves) Workspace::Get().TryMatchDesignWithWaves();
162170
for (Panel *p : panels_) {
163-
p->HandleReloadedWaves();
171+
if (layout_.has_design) p->HandleReloadedDesign();
172+
if (layout_.has_waves) p->HandleReloadedWaves();
164173
}
165-
// Force a refresh on the signals in the refreshed wave data tree.
166-
if (const auto scope = wave_tree_panel_->ScopeForSignals()) {
167-
wave_signals_panel_->SetScope(*scope);
174+
if (layout_.has_waves) {
175+
// Force a refresh on the signals in the refreshed wave data tree.
176+
if (const auto scope = wave_tree_panel_->ScopeForSignals()) {
177+
wave_signals_panel_->SetScope(*scope);
178+
}
168179
}
180+
return true;
169181
}
170182

171183
void UI::EventLoop() {
@@ -282,11 +294,7 @@ void UI::EventLoop() {
282294
}
283295
break;
284296
case 0x12: // ctrl-R
285-
if (layout_.has_waves && (panels_[focused_panel_idx_] == waves_panel_.get() ||
286-
panels_[focused_panel_idx_] == wave_tree_panel_.get() ||
287-
panels_[focused_panel_idx_] == wave_signals_panel_.get())) {
288-
ReloadWaves();
289-
}
297+
quit = !Reload();
290298
break;
291299
case 0x9: // tab
292300
case 0x161: { // shift-tab
@@ -358,6 +366,8 @@ void UI::EventLoop() {
358366
if (quit) break;
359367
Draw();
360368
}
369+
// Cleanup ncurses
370+
endwin();
361371
}
362372

363373
void UI::DrawHelp(int panel_idx) const {
@@ -550,7 +560,7 @@ void UI::Draw() const {
550560
move(row, col);
551561
curs_set(1);
552562
} else {
553-
if (focused_panel->CursorLocation()) {
563+
if (focused_panel->CursorLocation() && error_message_.empty()) {
554564
const auto [row, col] = *focused_panel->CursorLocation();
555565
int x, y;
556566
getbegyx(focused_panel->Window(), y, x);

src/ui.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,17 @@
1010

1111
namespace sv {
1212

13-
// This is the main UI. It owns all UI sub elements, all of which decend from
14-
// sv::Panel. It responds to terminal resizing and attempts to keep the layout
15-
// properly balanced.
13+
// This is the main UI. It owns all UI sub elements, all of which decend from sv::Panel. It responds
14+
// to terminal resizing and attempts to keep the layout properly balanced.
1615
class UI {
1716
public:
1817
// Constructor enters NCurses mode.
1918
UI();
20-
~UI();
2119

22-
// This is the main event loop. This function doesn't return until the user
23-
// has pressed Ctrl-Q to exit the UI.
20+
// This is the main event loop. This function doesn't return until the user has pressed Ctrl-Q to
21+
// exit the UI. A closing message is returned, in case of some kind of error.
2422
void EventLoop();
23+
const std::string &FinalMessage() const { return final_message_; }
2524

2625
private:
2726
void CalcLayout(bool update_frac = false);
@@ -30,7 +29,8 @@ class UI {
3029
void UpdateTooltips();
3130
void Draw() const;
3231
void DrawHelp(int panel_idx) const;
33-
void ReloadWaves();
32+
// False if some fatal error requires abort.
33+
bool Reload();
3434

3535
std::unique_ptr<DesignTreePanel> design_tree_panel_;
3636
std::unique_ptr<SourcePanel> source_panel_;
@@ -65,6 +65,9 @@ class UI {
6565
// Search state.
6666
bool searching_ = false;
6767
TextInput search_box_;
68+
69+
// Terminating message for the user after the UI exits.
70+
std::string final_message_;
6871
};
6972

7073
} // namespace sv

src/workspace.cc

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,25 @@ const slang::SourceManager *Workspace::SourceManager() const {
2525
return slang_compilation_->getSourceManager();
2626
}
2727

28+
bool Workspace::ReParse() {
29+
slang_compilation_ = nullptr;
30+
design_root_ = nullptr;
31+
const bool parse_ok = ParseDesign(/*initial*/ false);
32+
const bool success = slang_driver_->diagEngine.getNumErrors() == 0;
33+
return parse_ok && success;
34+
}
35+
2836
bool Workspace::ParseDesign(int argc, char *argv[]) {
37+
command_line_.argc = argc;
38+
command_line_.argv = argv;
39+
return ParseDesign(/*initial*/ true);
40+
}
41+
42+
bool Workspace::ParseDesign(bool initial) {
2943
// Avoid invoking the slang compiler if only wave-reading options are specified.
3044
const bool try_read_design =
31-
!(argc == 3 && (std::strcmp(argv[1], "-w") == 0 || std::strcmp(argv[1], "--waves") == 0));
45+
!(command_line_.argc == 3 && (std::strcmp(command_line_.argv[1], "-w") == 0 ||
46+
std::strcmp(command_line_.argv[1], "--waves") == 0));
3247
slang_driver_ = std::make_unique<slang::driver::Driver>();
3348
// Additional options from simview:
3449
std::optional<bool> show_help;
@@ -41,36 +56,36 @@ bool Workspace::ParseDesign(int argc, char *argv[]) {
4156
"Retain 0-time transitions in the wave data. Normally pruned.");
4257

4358
slang_driver_->addStandardArgs();
44-
if (!slang_driver_->parseCommandLine(argc, argv)) return false;
45-
if (show_help == true) {
59+
if (!slang_driver_->parseCommandLine(command_line_.argc, command_line_.argv)) return false;
60+
if (show_help == true && initial) {
4661
std::cout << slang_driver_->cmdLine.getHelpText(
4762
"simview: a terminal-based verilog design browser and waves viewer.")
4863
<< "\n";
4964
return 0;
5065
}
5166
bool design_ok = false;
5267
if (try_read_design && slang_driver_->processOptions()) {
53-
std::cout << "Parsing files...\n";
68+
if (initial) std::cout << "Parsing files...\n";
5469
if (!slang_driver_->parseAllSources()) return false;
55-
std::cout << "Elaborating...\n";
70+
if (initial) std::cout << "Elaborating...\n";
5671
slang_compilation_ = slang_driver_->createCompilation();
57-
// TODO: This freezes the design and somehow makes traversal throw exceptions.
58-
// std::cout << "Analyzing...\n";
59-
// slang_analysis_ = slang_driver_->runAnalysis(*slang_compilation_);
6072
// This print all tops, and collects diagnostics.
61-
slang_driver_->reportCompilation(*slang_compilation_, /* quiet */ false);
62-
const bool success = slang_driver_->reportDiagnostics(/* quiet */ false);
63-
// Give the user a chance to see any errors before proceeding.
64-
if (!success) {
65-
std::cout << "Errors encountered, press Enter to continue anyway...\n";
66-
std::cin.get();
73+
slang_driver_->reportCompilation(*slang_compilation_, /* quiet */ !initial);
74+
if (initial) {
75+
const bool success = slang_driver_->reportDiagnostics(/* quiet */ !initial);
76+
// Give the user a chance to see any errors before proceeding.
77+
if (!success) {
78+
std::cout << "Errors encountered, press Enter to continue anyway...\n";
79+
std::cin.get();
80+
}
6781
}
6882
design_root_ = &slang_compilation_->getRoot();
6983
design_ok = true;
7084
}
7185

7286
bool waves_ok = false;
73-
if (waves_file) {
87+
// Waves are only read on initial load. There's a separate mechanism that triggers wave reload.
88+
if (initial && waves_file) {
7489
try {
7590
wave_data_ = WaveData::ReadWaveFile(*waves_file, *keep_glitches);
7691
waves_ok = true;

src/workspace.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Workspace {
3434

3535
// Parse the design and/or waves using command line arguments. Return true on success.
3636
bool ParseDesign(int argc, char *argv[]);
37+
bool ReParse();
3738

3839
const slang::ast::RootSymbol *Design() const { return design_root_; }
3940

@@ -67,6 +68,8 @@ class Workspace {
6768
Workspace();
6869
~Workspace();
6970

71+
bool ParseDesign(bool initial);
72+
7073
std::unique_ptr<slang::driver::Driver> slang_driver_;
7174
std::unique_ptr<slang::ast::Compilation> slang_compilation_;
7275
std::unique_ptr<slang::analysis::AnalysisManager> slang_analysis_;
@@ -76,6 +79,10 @@ class Workspace {
7679
const WaveData::SignalScope *matched_signal_scope_ = nullptr;
7780
// Wave time is used in source too, so it's held here.
7881
uint64_t wave_cursor_time_ = 0;
82+
struct {
83+
int argc;
84+
char **argv;
85+
} command_line_;
7986
};
8087

8188
} // namespace sv

0 commit comments

Comments
 (0)