├── endlos.png ├── endlos.pcf ├── Makefile ├── rtl └── blinky.v ├── LICENSE.md ├── README.md └── pcb └── mde.kicad_pro /endlos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/machdyne/endlos/HEAD/endlos.png -------------------------------------------------------------------------------- /endlos.pcf: -------------------------------------------------------------------------------- 1 | # Endlos V0 2 | # --------- 3 | 4 | # RGB LED 5 | set_io LED_R 40 6 | set_io LED_G 41 7 | set_io LED_B 39 8 | 9 | # PMOD_A 10 | set_io PMOD_A1 2 11 | set_io PMOD_A2 3 12 | set_io PMOD_A3 4 13 | set_io PMOD_A4 6 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | blinky: 2 | mkdir -p output 3 | yosys -q -p "synth_ice40 -top blinky -json output/blinky.json" \ 4 | rtl/blinky.v 5 | nextpnr-ice40 -q --up5k --package sg48 --pcf endlos.pcf \ 6 | --asc output/blinky.txt --json output/blinky.json \ 7 | --pcf-allow-unconstrained 8 | icebox_explain output/blinky.txt > output/blinky.ex 9 | icepack output/blinky.txt output/blinky.bin 10 | 11 | clean: 12 | rm -f output/* 13 | 14 | .PHONY: blinky clean 15 | -------------------------------------------------------------------------------- /rtl/blinky.v: -------------------------------------------------------------------------------- 1 | module blinky #() 2 | ( 3 | 4 | output LED_R, LED_G, LED_B, 5 | output PMOD_A1, PMOD_A2, PMOD_A3, PMOD_A4, 6 | 7 | ); 8 | 9 | wire clk; 10 | 11 | SB_HFOSC hfosc_i ( 12 | .CLKHFPU(1'b1), 13 | .CLKHFEN(1'b1), 14 | .CLKHF(clk) 15 | ); 16 | 17 | reg [26:0] counter = 0; 18 | 19 | assign LED_R = counter[26]; 20 | assign LED_G = counter[25]; 21 | assign LED_B = counter[24]; 22 | 23 | assign PMOD_A1 = counter[26]; 24 | assign PMOD_A2 = counter[25]; 25 | assign PMOD_A3 = counter[24]; 26 | assign PMOD_A4 = counter[23]; 27 | 28 | always @(posedge clk) begin 29 | 30 | counter <= counter + 1; 31 | 32 | end 33 | 34 | endmodule 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Lone Dynamics Corporation. All rights reserved. 2 | 3 | Redistribution and use in source, binary or physical forms, with or without 4 | modification, is permitted provided that the following condition is met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | THIS HARDWARE, SOFTWARE, DATA AND/OR DOCUMENTATION ("THE ASSETS") IS PROVIDED 10 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 11 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 12 | AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE ASSETS OR THE USE OR OTHER DEALINGS IN THE ASSETS. USE AT 15 | YOUR OWN RISK. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Endlos Computer 2 | 3 | Endlos is a 1-layer FPGA computer designed by Lone Dynamics Corporation. 4 | 5 | ![Endlos Computer](https://github.com/machdyne/endlos/blob/5ab7c421435a9b4e87dfb7068ffc22f6d31f5fcc/endlos.png) 6 | 7 | This repo contains schematics, PCB layouts, pinouts, example gateware and documentation. 8 | 9 | Find more information on the [Endlos product page](https://machdyne.com/product/endlos-computer/). 10 | 11 | ## Blinky 12 | 13 | Building the blinky example requires [Yosys](https://github.com/YosysHQ/yosys), [nextpnr-ice40](https://github.com/YosysHQ/nextpnr) and [IceStorm](https://github.com/YosysHQ/icestorm). 14 | 15 | ## Pinouts 16 | 17 | ### PMOD Socket (left, pins top-to-bottom) 18 | 19 | | Pin | Signal | Description | 20 | | ----| ------ | ----------- | 21 | | 1 | PMOD\_A1 | GPIO | 22 | | 2 | PMOD\_A2 | GPIO | 23 | | 3 | PMOD\_A3 | GPIO | 24 | | 4 | PMOD\_A4 | GPIO | 25 | | 5 | GND | Ground | 26 | | 6 | 3V3 | Power | 27 | 28 | ### MMOD Socket (bottom, pins left-to-right) 29 | 30 | | Pin | Signal | Description | 31 | | --- | ------ | ----------- | 32 | | 1 | CSPI\_SS | GPIO | 33 | | 2 | CSPI\_SO | GPIO | 34 | | 3 | CSPI\_SI | GPIO | 35 | | 4 | CSPI\_SCK | GPIO | 36 | | 5 | GND | Ground | 37 | | 6 | 3V3 | Power | 38 | 39 | ### Audio/Video Jack 40 | 41 | | Pin | Description | 42 | | --- | ----------- | 43 | | T | Mono Audio | 44 | | R1 | Mono Audio | 45 | | R2 | Video | 46 | | S | Ground | 47 | 48 | ## Board Revisions 49 | 50 | | Revision | Notes | 51 | | -------- | ----- | 52 | | V0 | Initial version | 53 | 54 | Note #1: All of the functionality hasn't been tested yet, only configuration from MMOD and blinky on an LED PMOD. 55 | 56 | Note #2: There should probably be a 10K pull-up on RAM\_SS, but configuration appears to work without it. 57 | 58 | Note #3: Kapton tape is recommended under front of the USB-B connector. 59 | 60 | ## License 61 | 62 | The contents of this repo are released under the [Lone Dynamics Open License](LICENSE.md). 63 | 64 | Note: You can use these designs for commercial purposes but we ask that instead of producing exact clones, that you either replace our trademarks and logos with your own or add your own next to ours. 65 | -------------------------------------------------------------------------------- /pcb/mde.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.1, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.15, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 3.2, 41 | "height": 3.6, 42 | "width": 3.6 43 | }, 44 | "silk_line_width": 0.15, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.15, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "45_degree_only": false, 52 | "min_clearance": 0.508 53 | } 54 | }, 55 | "diff_pair_dimensions": [ 56 | { 57 | "gap": 0.0, 58 | "via_gap": 0.0, 59 | "width": 0.0 60 | } 61 | ], 62 | "drc_exclusions": [], 63 | "meta": { 64 | "version": 2 65 | }, 66 | "rule_severities": { 67 | "annular_width": "error", 68 | "clearance": "error", 69 | "connection_width": "warning", 70 | "copper_edge_clearance": "error", 71 | "copper_sliver": "warning", 72 | "courtyards_overlap": "error", 73 | "diff_pair_gap_out_of_range": "error", 74 | "diff_pair_uncoupled_length_too_long": "error", 75 | "drill_out_of_range": "error", 76 | "duplicate_footprints": "warning", 77 | "extra_footprint": "warning", 78 | "footprint": "error", 79 | "footprint_symbol_mismatch": "warning", 80 | "footprint_type_mismatch": "warning", 81 | "hole_clearance": "error", 82 | "hole_near_hole": "error", 83 | "holes_co_located": "warning", 84 | "invalid_outline": "error", 85 | "isolated_copper": "warning", 86 | "item_on_disabled_layer": "error", 87 | "items_not_allowed": "error", 88 | "length_out_of_range": "error", 89 | "lib_footprint_issues": "warning", 90 | "lib_footprint_mismatch": "warning", 91 | "malformed_courtyard": "error", 92 | "microvia_drill_out_of_range": "error", 93 | "missing_courtyard": "ignore", 94 | "missing_footprint": "warning", 95 | "net_conflict": "warning", 96 | "npth_inside_courtyard": "ignore", 97 | "padstack": "error", 98 | "pth_inside_courtyard": "ignore", 99 | "shorting_items": "error", 100 | "silk_edge_clearance": "warning", 101 | "silk_over_copper": "warning", 102 | "silk_overlap": "warning", 103 | "skew_out_of_range": "error", 104 | "solder_mask_bridge": "error", 105 | "starved_thermal": "error", 106 | "text_height": "warning", 107 | "text_thickness": "warning", 108 | "through_hole_pad_without_hole": "error", 109 | "too_many_vias": "error", 110 | "track_dangling": "warning", 111 | "track_width": "error", 112 | "tracks_crossing": "error", 113 | "unconnected_items": "error", 114 | "unresolved_variable": "error", 115 | "via_dangling": "warning", 116 | "zone_has_empty_net": "error", 117 | "zones_intersect": "error" 118 | }, 119 | "rules": { 120 | "allow_blind_buried_vias": false, 121 | "allow_microvias": false, 122 | "max_error": 0.005, 123 | "min_clearance": 0.2, 124 | "min_connection": 0.0, 125 | "min_copper_edge_clearance": 1.0, 126 | "min_hole_clearance": 0.25, 127 | "min_hole_to_hole": 0.25, 128 | "min_microvia_diameter": 0.2, 129 | "min_microvia_drill": 0.1, 130 | "min_resolved_spokes": 1, 131 | "min_silk_clearance": 0.0, 132 | "min_text_height": 0.8, 133 | "min_text_thickness": 0.08, 134 | "min_through_hole_diameter": 0.3, 135 | "min_track_width": 0.2, 136 | "min_via_annular_width": 0.05, 137 | "min_via_diameter": 0.4, 138 | "solder_mask_clearance": 0.0, 139 | "solder_mask_min_width": 0.0, 140 | "solder_mask_to_copper_clearance": 0.0, 141 | "use_height_for_length_calcs": true 142 | }, 143 | "teardrop_options": [ 144 | { 145 | "td_onpadsmd": true, 146 | "td_onroundshapesonly": false, 147 | "td_ontrackend": false, 148 | "td_onviapad": true 149 | } 150 | ], 151 | "teardrop_parameters": [ 152 | { 153 | "td_allow_use_two_tracks": true, 154 | "td_curve_segcount": 0, 155 | "td_height_ratio": 1.0, 156 | "td_length_ratio": 0.5, 157 | "td_maxheight": 2.0, 158 | "td_maxlen": 1.0, 159 | "td_on_pad_in_zone": false, 160 | "td_target_name": "td_round_shape", 161 | "td_width_to_size_filter_ratio": 0.9 162 | }, 163 | { 164 | "td_allow_use_two_tracks": true, 165 | "td_curve_segcount": 0, 166 | "td_height_ratio": 1.0, 167 | "td_length_ratio": 0.5, 168 | "td_maxheight": 2.0, 169 | "td_maxlen": 1.0, 170 | "td_on_pad_in_zone": false, 171 | "td_target_name": "td_rect_shape", 172 | "td_width_to_size_filter_ratio": 0.9 173 | }, 174 | { 175 | "td_allow_use_two_tracks": true, 176 | "td_curve_segcount": 0, 177 | "td_height_ratio": 1.0, 178 | "td_length_ratio": 0.5, 179 | "td_maxheight": 2.0, 180 | "td_maxlen": 1.0, 181 | "td_on_pad_in_zone": false, 182 | "td_target_name": "td_track_end", 183 | "td_width_to_size_filter_ratio": 0.9 184 | } 185 | ], 186 | "track_widths": [ 187 | 0.0, 188 | 0.25, 189 | 0.5 190 | ], 191 | "tuning_pattern_settings": { 192 | "diff_pair_defaults": { 193 | "corner_radius_percentage": 80, 194 | "corner_style": 1, 195 | "max_amplitude": 1.0, 196 | "min_amplitude": 0.2, 197 | "single_sided": false, 198 | "spacing": 1.0 199 | }, 200 | "diff_pair_skew_defaults": { 201 | "corner_radius_percentage": 80, 202 | "corner_style": 1, 203 | "max_amplitude": 1.0, 204 | "min_amplitude": 0.2, 205 | "single_sided": false, 206 | "spacing": 0.6 207 | }, 208 | "single_track_defaults": { 209 | "corner_radius_percentage": 80, 210 | "corner_style": 1, 211 | "max_amplitude": 1.0, 212 | "min_amplitude": 0.2, 213 | "single_sided": false, 214 | "spacing": 0.6 215 | } 216 | }, 217 | "via_dimensions": [ 218 | { 219 | "diameter": 0.0, 220 | "drill": 0.0 221 | }, 222 | { 223 | "diameter": 0.6, 224 | "drill": 0.3 225 | } 226 | ], 227 | "zones_allow_external_fillets": false, 228 | "zones_use_no_outline": true 229 | }, 230 | "ipc2581": { 231 | "dist": "", 232 | "distpn": "", 233 | "internal_id": "", 234 | "mfg": "", 235 | "mpn": "" 236 | }, 237 | "layer_presets": [], 238 | "viewports": [] 239 | }, 240 | "boards": [], 241 | "cvpcb": { 242 | "equivalence_files": [] 243 | }, 244 | "erc": { 245 | "erc_exclusions": [], 246 | "meta": { 247 | "version": 0 248 | }, 249 | "pin_map": [ 250 | [ 251 | 0, 252 | 0, 253 | 0, 254 | 0, 255 | 0, 256 | 0, 257 | 1, 258 | 0, 259 | 0, 260 | 0, 261 | 0, 262 | 2 263 | ], 264 | [ 265 | 0, 266 | 2, 267 | 0, 268 | 1, 269 | 0, 270 | 0, 271 | 1, 272 | 0, 273 | 2, 274 | 2, 275 | 2, 276 | 2 277 | ], 278 | [ 279 | 0, 280 | 0, 281 | 0, 282 | 0, 283 | 0, 284 | 0, 285 | 1, 286 | 0, 287 | 1, 288 | 0, 289 | 1, 290 | 2 291 | ], 292 | [ 293 | 0, 294 | 1, 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 1, 300 | 1, 301 | 2, 302 | 1, 303 | 1, 304 | 2 305 | ], 306 | [ 307 | 0, 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 1, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 2 319 | ], 320 | [ 321 | 0, 322 | 0, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 0, 331 | 0, 332 | 2 333 | ], 334 | [ 335 | 1, 336 | 1, 337 | 1, 338 | 1, 339 | 1, 340 | 0, 341 | 1, 342 | 1, 343 | 1, 344 | 1, 345 | 1, 346 | 2 347 | ], 348 | [ 349 | 0, 350 | 0, 351 | 0, 352 | 1, 353 | 0, 354 | 0, 355 | 1, 356 | 0, 357 | 0, 358 | 0, 359 | 0, 360 | 2 361 | ], 362 | [ 363 | 0, 364 | 2, 365 | 1, 366 | 2, 367 | 0, 368 | 0, 369 | 1, 370 | 0, 371 | 2, 372 | 2, 373 | 2, 374 | 2 375 | ], 376 | [ 377 | 0, 378 | 2, 379 | 0, 380 | 1, 381 | 0, 382 | 0, 383 | 1, 384 | 0, 385 | 2, 386 | 0, 387 | 0, 388 | 2 389 | ], 390 | [ 391 | 0, 392 | 2, 393 | 1, 394 | 1, 395 | 0, 396 | 0, 397 | 1, 398 | 0, 399 | 2, 400 | 0, 401 | 0, 402 | 2 403 | ], 404 | [ 405 | 2, 406 | 2, 407 | 2, 408 | 2, 409 | 2, 410 | 2, 411 | 2, 412 | 2, 413 | 2, 414 | 2, 415 | 2, 416 | 2 417 | ] 418 | ], 419 | "rule_severities": { 420 | "bus_definition_conflict": "error", 421 | "bus_entry_needed": "error", 422 | "bus_label_syntax": "error", 423 | "bus_to_bus_conflict": "error", 424 | "bus_to_net_conflict": "error", 425 | "conflicting_netclasses": "error", 426 | "different_unit_footprint": "error", 427 | "different_unit_net": "error", 428 | "duplicate_reference": "error", 429 | "duplicate_sheet_names": "error", 430 | "endpoint_off_grid": "warning", 431 | "extra_units": "error", 432 | "global_label_dangling": "warning", 433 | "hier_label_mismatch": "error", 434 | "label_dangling": "error", 435 | "lib_symbol_issues": "warning", 436 | "missing_bidi_pin": "warning", 437 | "missing_input_pin": "warning", 438 | "missing_power_pin": "error", 439 | "missing_unit": "warning", 440 | "multiple_net_names": "warning", 441 | "net_not_bus_member": "warning", 442 | "no_connect_connected": "warning", 443 | "no_connect_dangling": "warning", 444 | "pin_not_connected": "error", 445 | "pin_not_driven": "error", 446 | "pin_to_pin": "warning", 447 | "power_pin_not_driven": "error", 448 | "similar_labels": "warning", 449 | "simulation_model_issue": "ignore", 450 | "unannotated": "error", 451 | "unit_value_mismatch": "error", 452 | "unresolved_variable": "error", 453 | "wire_dangling": "error" 454 | } 455 | }, 456 | "libraries": { 457 | "pinned_footprint_libs": [], 458 | "pinned_symbol_libs": [] 459 | }, 460 | "meta": { 461 | "filename": "mde.kicad_pro", 462 | "version": 1 463 | }, 464 | "net_settings": { 465 | "classes": [ 466 | { 467 | "bus_width": 12, 468 | "clearance": 0.2, 469 | "diff_pair_gap": 0.25, 470 | "diff_pair_via_gap": 0.25, 471 | "diff_pair_width": 0.25, 472 | "line_style": 0, 473 | "microvia_diameter": 0.3, 474 | "microvia_drill": 0.1, 475 | "name": "Default", 476 | "pcb_color": "rgba(0, 0, 0, 0.000)", 477 | "schematic_color": "rgba(0, 0, 0, 0.000)", 478 | "track_width": 0.25, 479 | "via_diameter": 0.6, 480 | "via_drill": 0.4, 481 | "wire_width": 6 482 | } 483 | ], 484 | "meta": { 485 | "version": 3 486 | }, 487 | "net_colors": null, 488 | "netclass_assignments": null, 489 | "netclass_patterns": [] 490 | }, 491 | "pcbnew": { 492 | "last_paths": { 493 | "gencad": "", 494 | "idf": "", 495 | "netlist": "", 496 | "plot": "", 497 | "pos_files": "", 498 | "specctra_dsn": "", 499 | "step": "", 500 | "svg": "", 501 | "vrml": "" 502 | }, 503 | "page_layout_descr_file": "" 504 | }, 505 | "schematic": { 506 | "annotate_start_num": 0, 507 | "bom_export_filename": "", 508 | "bom_fmt_presets": [], 509 | "bom_fmt_settings": { 510 | "field_delimiter": ",", 511 | "keep_line_breaks": false, 512 | "keep_tabs": false, 513 | "name": "CSV", 514 | "ref_delimiter": ",", 515 | "ref_range_delimiter": "", 516 | "string_delimiter": "\"" 517 | }, 518 | "bom_presets": [], 519 | "bom_settings": { 520 | "exclude_dnp": false, 521 | "fields_ordered": [ 522 | { 523 | "group_by": false, 524 | "label": "Reference", 525 | "name": "Reference", 526 | "show": true 527 | }, 528 | { 529 | "group_by": true, 530 | "label": "Value", 531 | "name": "Value", 532 | "show": true 533 | }, 534 | { 535 | "group_by": false, 536 | "label": "Datasheet", 537 | "name": "Datasheet", 538 | "show": true 539 | }, 540 | { 541 | "group_by": false, 542 | "label": "Footprint", 543 | "name": "Footprint", 544 | "show": true 545 | }, 546 | { 547 | "group_by": false, 548 | "label": "Qty", 549 | "name": "${QUANTITY}", 550 | "show": true 551 | }, 552 | { 553 | "group_by": true, 554 | "label": "DNP", 555 | "name": "${DNP}", 556 | "show": true 557 | } 558 | ], 559 | "filter_string": "", 560 | "group_symbols": true, 561 | "name": "Grouped By Value", 562 | "sort_asc": true, 563 | "sort_field": "Reference" 564 | }, 565 | "connection_grid_size": 50.0, 566 | "drawing": { 567 | "dashed_lines_dash_length_ratio": 12.0, 568 | "dashed_lines_gap_length_ratio": 3.0, 569 | "default_line_thickness": 6.0, 570 | "default_text_size": 50.0, 571 | "field_names": [], 572 | "intersheets_ref_own_page": false, 573 | "intersheets_ref_prefix": "", 574 | "intersheets_ref_short": false, 575 | "intersheets_ref_show": false, 576 | "intersheets_ref_suffix": "", 577 | "junction_size_choice": 3, 578 | "label_size_ratio": 0.375, 579 | "operating_point_overlay_i_precision": 3, 580 | "operating_point_overlay_i_range": "~A", 581 | "operating_point_overlay_v_precision": 3, 582 | "operating_point_overlay_v_range": "~V", 583 | "overbar_offset_ratio": 1.23, 584 | "pin_symbol_size": 25.0, 585 | "text_offset_ratio": 0.15 586 | }, 587 | "legacy_lib_dir": "", 588 | "legacy_lib_list": [], 589 | "meta": { 590 | "version": 1 591 | }, 592 | "net_format_name": "", 593 | "ngspice": { 594 | "fix_include_paths": true, 595 | "fix_passive_vals": false, 596 | "meta": { 597 | "version": 0 598 | }, 599 | "model_mode": 0, 600 | "workbook_filename": "" 601 | }, 602 | "page_layout_descr_file": "", 603 | "plot_directory": "", 604 | "spice_adjust_passive_values": false, 605 | "spice_current_sheet_as_root": false, 606 | "spice_external_command": "spice \"%I\"", 607 | "spice_model_current_sheet_as_root": true, 608 | "spice_save_all_currents": false, 609 | "spice_save_all_dissipations": false, 610 | "spice_save_all_voltages": false, 611 | "subpart_first_id": 65, 612 | "subpart_id_separator": 0 613 | }, 614 | "sheets": [ 615 | [ 616 | "075784d7-69f7-4217-b482-acb96db1ff7b", 617 | "Root" 618 | ] 619 | ], 620 | "text_variables": {} 621 | } 622 | --------------------------------------------------------------------------------